I'm currently trying to test a ListView on android with Espresso, with a custom ArrayAdapter.
Here is the test :
final AssociationsListActivity listActivity = getActivity();
final Association association1 = new Association(ASSOCIATION_NAMES[0], ASSOCIATION_DESCRIPTIONS[0]);
final Association association2 = new Association(ASSOCIATION_NAMES[1], ASSOCIATION_DESCRIPTIONS[1]);
final Association association3 = new Association(ASSOCIATION_NAMES[2], ASSOCIATION_DESCRIPTIONS[2]);
listActivity.addAssociation(association1);
listActivity.addAssociation(association2);
listActivity.addAssociation(association3);
assertTrue(listActivity.getAssociationsList().size() == 3);
onView(withId(R.id.associationsListView)).check(matches(isDisplayed()));
onData(anything())
.inAdapterView(withId(R.id.associationsListView))
.atPosition(0)
.perform(click());
assertCurrentActivityIsInstanceOf(AssociationsListActivityTest.this
, AssociationsPageActivity.class);
The method listActivity.addAssociation(association); simply does associationArrayAdapter.add(association);.
The ArrayAdapteris used to add elements to a ListView(visible by default) with the id R.id.associationsListView
When I run this test, I get the following error :
android.support.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'is displayed on the screen to the user' doesn't match the selected view.
Expected: is displayed on the screen to the user
Got: "ListView{id=2131427414, res-name=associationsListView, visibility=VISIBLE, width=996, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0, child-count=0}"
Which is caused by the following assertion, onView(withId(R.id.associationsListView)).check(matches(isDisplayed()));
Also when commenting this assertion, the next one causes the following exception :
android.support.test.espresso.PerformException: Error performing 'load adapter data' on view 'with id: ch.epfl.sweng.project:id/associationsListView'.
...
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(is assignable from class: class android.widget.AdapterView and is displayed on the screen to the user)
Target view: "ListView{id=2131427414, res-name=associationsListView, visibility=VISIBLE, width=996, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=42.0, y=42.0, child-count=0}"
I went through a lot of stack overflow threads covering those exception, I replaced those tests by other equivalents but those errors still show up I don't know what to do! Thanks for any help!
I found what was wrong!
My method to add elements to the ArrayAdapter used runOnUIThread but it seems that the test was not waiting for this method, thus the associationsListViewwas considered as not displayed to the user ! So I fixed my method listActivity.addAssociation(Association association) like this :
public void addAssociation(final Association association) {
final CountDownLatch latch = new CountDownLatch(1);
runOnUiThread(new Runnable() {
#Override
public void run() {
if (!associationList.contains(association)) {
associationArrayAdapter.add(association);
}
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!associationList.contains(association)) {
associationList.add(association);
}
}
All parts of the test now pass :)
Related
https://github.com/carlb710/myProgram
I'm using IntelliJ Swing GUI editor to make the front-end of my program. Based on the int value returned by a spinner, certain elements of the GUI are shown or hidden. Every time I change that int value and restart the program, one of many exceptions is thrown, all NullPointer Exceptions. I think they are always in the same thread, it's just the reason that varies.
Error usually looks something like these below:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot read field "top" because "topInsets" is null
at java.desktop/com.apple.laf.AquaTabbedPaneCopyFromBasicUI.rotateInsets(AquaTabbedPaneCopyFromBasicUI.java:2000)
at java.desktop/com.apple.laf.AquaTabbedPaneCopyFromBasicUI.getTabAreaInsets(AquaTabbedPaneCopyFromBasicUI.java:1681)
at java.desktop/com.apple.laf.AquaTabbedPaneUI$AquaTruncatingTabbedPaneLayout.superCalculateTabRects(AquaTabbedPaneUI.java:1133)
at java.desktop/com.apple.laf.AquaTabbedPaneUI$AquaTruncatingTabbedPaneLayout.calculateTabRects(AquaTabbedPaneUI.java:1091)
at java.desktop/com.apple.laf.AquaTabbedPaneCopyFromBasicUI$TabbedPaneLayout.calculateLayoutInfo(AquaTabbedPaneCopyFromBasicUI.java:2322)
at java.desktop/com.apple.laf.AquaTabbedPaneCopyFromBasicUI$TabbedPaneLayout.layoutContainer(AquaTabbedPaneCopyFromBasicUI.java:2226)
at java.desktop/java.awt.Container.layout(Container.java:1538)
at java.desktop/java.awt.Container.doLayout(Container.java:1527)
at java.desktop/java.awt.Container.validateTree(Container.java:1723)
at java.desktop/java.awt.Container.validate(Container.java:1658)
at java.desktop/com.apple.laf.AquaTabbedPaneCopyFromBasicUI.ensureCurrentLayout(AquaTabbedPaneCopyFromBasicUI.java:1335)
at java.desktop/com.apple.laf.AquaTabbedPaneUI.paint(AquaTabbedPaneUI.java:148)
at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:797)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1074)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1083)
at java.desktop/javax.swing.JLayeredPane.paint(JLayeredPane.java:586)
at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:907)
at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5271)
at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1643)
at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1618)
at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1556)
at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1323)
at java.desktop/javax.swing.JComponent.paint(JComponent.java:1060)
at java.desktop/java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:39)
at java.desktop/sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:75)
at java.desktop/sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:112)
at java.desktop/java.awt.Container.paint(Container.java:2003)
at java.desktop/java.awt.Window.paint(Window.java:3949)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:876)
at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:848)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:848)
at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:823)
at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:772)
at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1884)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
The exceptions seem to be thrown when I run the following code in my main class to create the JFrame instance for my program:
App window = null;
try{
window = new App();
} catch(NullPointerException e){
e.printStackTrace();
}
JFrame frame = new JFrame("Java QA Checker");
frame.setContentPane(window.landing);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
Font font = new Font("Helvetica", Font.PLAIN, 16);
My GUI constructor looks like this where I think the issue is coming from:
//listener for spinner, changes number of elements on screen
spinner1.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
numOfFiles = (Integer) spinner1.getValue();
//switch statement sets number of elements for file settings visible or not based on spinner value up to 12
try {
switch(numOfFiles){
case 0:{
file1OriginLabel.setVisible(true);
file2OriginLabel.setVisible(true);
file3OriginLabel.setVisible(true);
file4OriginLabel.setVisible(true);
file5OriginLabel.setVisible(true);
file6OriginLabel.setVisible(true);
file7OriginLabel.setVisible(true);
file8OriginLabel.setVisible(true);
file9OriginLabel.setVisible(true);
file10OriginLabel.setVisible(true);
file11OriginLabel.setVisible(true);
file12OriginLabel.setVisible(true);
file1OriginInputted.setVisible(true);
file2OriginInputted.setVisible(true);
file3OriginInputted.setVisible(true);
file4OriginInputted.setVisible(true);
file5OriginInputted.setVisible(true);
file6OriginInputted.setVisible(true);
file7OriginInputted.setVisible(true);
file8OriginInputted.setVisible(true);
file9OriginInputted.setVisible(true);
file10OriginInputted.setVisible(true);
file11OriginInputted.setVisible(true);
file12OriginInputted.setVisible(true);
addFileButton1.setVisible(true);
addFileButton2.setVisible(true);
addFileButton3.setVisible(true);
addFileButton4.setVisible(true);
addFileButton5.setVisible(true);
addFileButton6.setVisible(true);
addFileButton7.setVisible(true);
addFileButton8.setVisible(true);
addFileButton9.setVisible(true);
addFileButton10.setVisible(true);
addFileButton11.setVisible(true);
addFileButton12.setVisible(true);
filePathLabel1.setVisible(true);
filePathLabel2.setVisible(true);
filePathLabel3.setVisible(true);
filePathLabel4.setVisible(true);
filePathLabel5.setVisible(true);
filePathLabel6.setVisible(true);
filePathLabel7.setVisible(true);
filePathLabel8.setVisible(true);
filePathLabel9.setVisible(true);
filePathLabel10.setVisible(true);
filePathLabel11.setVisible(true);
filePathLabel12.setVisible(true);
} break;
case 1:{
file1OriginLabel.setVisible(true);
file2OriginLabel.setVisible(false);
file3OriginLabel.setVisible(false);
file4OriginLabel.setVisible(false);
file5OriginLabel.setVisible(false);
file6OriginLabel.setVisible(false);
file7OriginLabel.setVisible(false);
file8OriginLabel.setVisible(false);
file9OriginLabel.setVisible(false);
file10OriginLabel.setVisible(false);
file11OriginLabel.setVisible(false);
file12OriginLabel.setVisible(false);
file1OriginInputted.setVisible(true);
file2OriginInputted.setVisible(false);
file3OriginInputted.setVisible(false);
file4OriginInputted.setVisible(false);
file5OriginInputted.setVisible(false);
file6OriginInputted.setVisible(false);
file7OriginInputted.setVisible(false);
file8OriginInputted.setVisible(false);
file9OriginInputted.setVisible(false);
file10OriginInputted.setVisible(false);
file11OriginInputted.setVisible(false);
file12OriginInputted.setVisible(false);
addFileButton1.setVisible(true);
addFileButton2.setVisible(false);
addFileButton3.setVisible(false);
addFileButton4.setVisible(false);
addFileButton5.setVisible(false);
addFileButton6.setVisible(false);
addFileButton7.setVisible(false);
addFileButton8.setVisible(false);
addFileButton9.setVisible(false);
addFileButton10.setVisible(false);
addFileButton11.setVisible(false);
addFileButton12.setVisible(false);
filePathLabel1.setVisible(true);
filePathLabel2.setVisible(false);
filePathLabel3.setVisible(false);
filePathLabel4.setVisible(false);
filePathLabel5.setVisible(false);
filePathLabel6.setVisible(false);
filePathLabel7.setVisible(false);
filePathLabel8.setVisible(false);
filePathLabel9.setVisible(false);
filePathLabel10.setVisible(false);
filePathLabel11.setVisible(false);
filePathLabel12.setVisible(false);
}break;
case 2:{
file1OriginLabel.setVisible(true);
file2OriginLabel.setVisible(true);
file3OriginLabel.setVisible(false);
file4OriginLabel.setVisible(false);
file5OriginLabel.setVisible(false);
file6OriginLabel.setVisible(false);
file7OriginLabel.setVisible(false);
file8OriginLabel.setVisible(false);
file9OriginLabel.setVisible(false);
file10OriginLabel.setVisible(false);
file11OriginLabel.setVisible(false);
file12OriginLabel.setVisible(false);
file1OriginInputted.setVisible(true);
file2OriginInputted.setVisible(true);
file3OriginInputted.setVisible(false);
file4OriginInputted.setVisible(false);
file5OriginInputted.setVisible(false);
file6OriginInputted.setVisible(false);
file7OriginInputted.setVisible(false);
file8OriginInputted.setVisible(false);
file9OriginInputted.setVisible(false);
file10OriginInputted.setVisible(false);
file11OriginInputted.setVisible(false);
file12OriginInputted.setVisible(false);
addFileButton1.setVisible(true);
addFileButton2.setVisible(true);
addFileButton3.setVisible(false);
addFileButton4.setVisible(false);
addFileButton5.setVisible(false);
addFileButton6.setVisible(false);
addFileButton7.setVisible(false);
addFileButton8.setVisible(false);
addFileButton9.setVisible(false);
addFileButton10.setVisible(false);
addFileButton11.setVisible(false);
addFileButton12.setVisible(false);
filePathLabel1.setVisible(true);
filePathLabel2.setVisible(true);
filePathLabel3.setVisible(false);
filePathLabel4.setVisible(false);
filePathLabel5.setVisible(false);
filePathLabel6.setVisible(false);
filePathLabel7.setVisible(false);
filePathLabel8.setVisible(false);
filePathLabel9.setVisible(false);
filePathLabel10.setVisible(false);
filePathLabel11.setVisible(false);
filePathLabel12.setVisible(false);
} break;
case 3:{
file1OriginLabel.setVisible(true);
file2OriginLabel.setVisible(true);
file3OriginLabel.setVisible(true);
file4OriginLabel.setVisible(false);
file5OriginLabel.setVisible(false);
file6OriginLabel.setVisible(false);
file7OriginLabel.setVisible(false);
file8OriginLabel.setVisible(false);
file9OriginLabel.setVisible(false);
file10OriginLabel.setVisible(false);
file11OriginLabel.setVisible(false);
file12OriginLabel.setVisible(false);
file1OriginInputted.setVisible(true);
file2OriginInputted.setVisible(true);
file3OriginInputted.setVisible(true);
file4OriginInputted.setVisible(false);
file5OriginInputted.setVisible(false);
file6OriginInputted.setVisible(false);
file7OriginInputted.setVisible(false);
file8OriginInputted.setVisible(false);
file9OriginInputted.setVisible(false);
file10OriginInputted.setVisible(false);
file11OriginInputted.setVisible(false);
file12OriginInputted.setVisible(false);
addFileButton1.setVisible(true);
addFileButton2.setVisible(true);
addFileButton3.setVisible(true);
addFileButton4.setVisible(false);
addFileButton5.setVisible(false);
addFileButton6.setVisible(false);
addFileButton7.setVisible(false);
addFileButton8.setVisible(false);
addFileButton9.setVisible(false);
addFileButton10.setVisible(false);
addFileButton11.setVisible(false);
addFileButton12.setVisible(false);
filePathLabel1.setVisible(true);
filePathLabel2.setVisible(true);
filePathLabel3.setVisible(true);
filePathLabel4.setVisible(false);
filePathLabel5.setVisible(false);
filePathLabel6.setVisible(false);
filePathLabel7.setVisible(false);
filePathLabel8.setVisible(false);
filePathLabel9.setVisible(false);
filePathLabel10.setVisible(false);
filePathLabel11.setVisible(false);
filePathLabel12.setVisible(false);
}break;
case 4:{..
} break;
//this goes until case 12, each just continues to make one more item .setVisible = true until all 12 elements are visible.
default: break;
}
} catch(NullPointerException e1){
e1.printStackTrace();
SwingUtilities.updateComponentTreeUI(landing);
}
SwingUtilities.updateComponentTreeUI(landing);
}
});
//additional code here that shouldn't be relevant to the issue
} ```
My issue was coming from the Main class that instantiated my GUI. I set the look and feel after creating the JFrame, when it should have been done before creating any GUI elements. This fixed my problem
I have an Augmented Reality application where ARObject is a POJO:
class ARObject {
CompletableFuture<Texture> texture;
CompletableFuture<Material> material;
ModelRenderable renderable;
void setTexture(CompletableFuture<Texture> texture) {
this.texture = texture;
}
CompletableFuture<Texture> getTexture() {
return texture;
}
void setMaterial(CompletableFuture<Material> material) {
this.material = material;
}
CompletableFuture<Material> getMaterial() {
return material;
}
}
The scene is composed in real-time. During that procedure the Texture objects need to be built and then the Material objects based on the Texture objects. Once the Material is ready, then the ShapeFactory can be used to spawn the actual AR objects (as a form Renderable). That means that the build logic contains two CompletableFutures nested into each other for each AR object:
for (ARObject arObject : arObjects) {
Texture.Builder textureBuilder = Texture.builder();
textureBuilder.setSource(context, arObject.resourceId);
CompletableFuture<Texture> texturePromise = textureBuilder.build(); // Future #1
arObject.setTexture(texturePromise);
texturePromise.thenAccept(texture -> {
CompletableFuture<Material> materialPromise =
MaterialFactory.makeOpaqueWithTexture(context, texture); // Future #2
arObject.setMaterial(materialPromise);
});
}
One way to complete the scene build is to wait until all of the CompletableFutures are done, and then the ShapeFactory step can come.
I tried to use .get() on the Futures, but that would not just completely butcher the parallelism offered by the async calls, but it did also lock up the app because I assume it caused the wait on the UI thread.
Arrays.stream(arObjectList).forEach(a -> {
try {
a.getTexture().get();
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Texture CompletableFuture waiting problem " + e.toString());
}
});
Arrays.stream(arObjectList).forEach(a -> {
try {
a.getMaterial().get();
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Material CompletableFuture waiting problem " + e.toString());
}
});
I broke the build procedure up to several functions which call each other in a call chain. The chain is the following:
populateScene
afterTexturesLoaded
afterTexturesSet
waitForMaterials
afterMaterialsLoaded
private void afterMaterialsLoaded() {
// Step 3: composing scene objects
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());
for (ARObject arObject : arObjectList) {
try {
Material textureMaterial = arObject.getMaterial().get();
RunnableShapeBuilder shapeBuilder = new RunnableShapeBuilder(arObject, this, textureMaterial);
mainHandler.post(shapeBuilder);
}
catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Scene populating exception " + e.toString());
}
}
}
private Long waitForMaterials() {
while (!Stream.of(arObjectList).allMatch(arObject -> arObject.getMaterial() != null)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
return 0L;
}
private void afterTexturesSet() {
boolean materialsDone = Stream.of(arObjectList).allMatch(arObject -> arObject.getMaterial() != null && arObject.getMaterial().isDone());
// If any of the materials are not loaded, then recurse until all are loaded.
if (!materialsDone) {
CompletableFuture<Texture>[] materialPromises =
Stream.of(arObjectList).map(ARObject::getMaterial).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(materialPromises)
.thenAccept((Void aVoid) -> afterMaterialsLoaded())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
} else {
afterMaterialsLoaded();
}
}
private void afterTexturesLoaded() {
// Step 2: material loading
CompletableFuture materialsSetPromise = CompletableFuture.supplyAsync(this::waitForMaterials);
CompletableFuture.allOf(materialsSetPromise)
.thenAccept((Void aVoid) -> afterTexturesSet())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
}
/**
* Called when the AugmentedImage is detected and should be rendered. A Sceneform node tree is
* created based on an Anchor created from the image.
*/
#SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
void populateScene() {
// Step 1: texture loading
boolean texturesDone = Stream.of(arObjectList).allMatch(arObject -> arObject.getTexture() != null && arObject.getTexture().isDone());
// If any of the textures are not loaded, then recurse until all are loaded.
if (!texturesDone) {
CompletableFuture<Texture>[] texturePromises =
Stream.of(arObjectList).map(ARObject::getTexture).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(texturePromises)
.thenAccept((Void aVoid) -> afterTexturesLoaded())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
} else {
afterTexturesLoaded();
}
}
There are several problem with this. First: it still kind of botches the asynchronous nature. In an ideal situation a corresponding material texture pair would be independently loaded and generated from the other pairs. In this latest version there are many meeting points in the execution flow, which does not correspond to the ideal independent scenario. Second: I even could not avoid the waitForMaterials step where I have ugly Thread.sleep(). Third: the code still fails overall, because at the last step when it comes to finally build the shapes from the loaded textures and materials, I'm gifted with an error java.lang.IllegalStateException: Must be called from the UI thread.. Because of this I put in another twist: RunnableShapeBuilder. With that there's no exception, but still nothing is shown on the scene, while the code get even more complicated.
class RunnableShapeBuilder implements Runnable {
ARObject arObject;
AnchorNode parentNode;
Material textureMaterial;
RunnableShapeBuilder(ARObject arObject, AnchorNode parentNode, Material textureMaterial) {
this.arObject = arObject;
this.parentNode = parentNode;
this.textureMaterial = textureMaterial;
}
#Override
public void run() {
arObject.renderable = ShapeFactory.makeCube(
new Vector3(0.5f, 1, 0.01f),
new Vector3(0.0f, 0.0f, 0.0f),
textureMaterial
);
...
}
}
The answer is: I do not have to wait for these nested CompletableFutures. While my scenario got more complex I assumed I'd have to wait until construction of the 3D Material and Texture is complete. The problem was in an off-topic place: although I set the AR object's anchor. The AR Object's anchor is derived from the hit test, and I'd need to set the parent of the anchor to the AR Scene. This last step was lost during some refactorings, and nothing warns you if this happens, just nothing show up on the AR Scene.
Focusing on the question itself: I highly disadvise to try to wait on any because it'll lead to pain and suffering only. Seek for a different alley of solution.
I tried to Implements an Eddystone beacon on android device using beacon-android library https://github.com/adriancretu/beacons-android#features as follows.
public class MainActivity extends AppCompatActivity {
EddystoneURL beacon = new EddystoneURL("www.github.com");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Beacons.initialize(this);
beacon.init(5, AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY,AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,0,"Eddie");
beacon.start();
UUID uuid = beacon.getUUID();
String name = beacon.getName();
Log.i("Log","name is "+name);
Log.i("Log",uuid.toString());
String url = beacon.getURL();
Log.i("Log",url);
Log.i("Log","App started");
int k = beacon.getActiveState();
Log.i("Log","active state : "+k);
int powerlvl = beacon.getTxPowerLevel();
Log.i("Log","Power Lvl : "+ powerlvl);
}
}
and I get logs as follows
2020-04-09 20:27:42.771 14922-14922/? I/Log: name is Eddie
2020-04-09 20:27:42.772 14922-14922/? I/Log: 21ac707d-2ef0-4578-aa52-f8b8020d97c3
2020-04-09 20:27:42.772 14922-14922/? I/Log: www.github.com
2020-04-09 20:27:42.772 14922-14922/? I/Log: App started
2020-04-09 20:27:42.772 14922-14922/? I/Log: active state : 0
2020-04-09 20:27:42.772 14922-14922/? I/Log: Power Lvl : 3
the problem is the emulated beacon is was not identified by beacon scanners. I really appreciate the help of yours. Thank you.
I realize this question is about building a transmitter with the beacons-android library, so a better answer would be to show how to make this work properly with that library.
However, if no solution is found and the OP is open to using the Android Beacon Library (similarly named but completely different) to accomplish this, the code below will do it:
try {
byte[] urlBytes = UrlBeaconUrlCompressor.compress("https://www.github.com"");
Identifier encodedUrlIdentifier = Identifier.fromBytes(urlBytes, 0, urlBytes.length, false);
ArrayList<Identifier> identifiers = new ArrayList<Identifier>();
identifiers.add(encodedUrlIdentifier);
beacon = new Beacon.Builder()
.setIdentifiers(identifiers)
.setTxPower(-59)
.build();
BeaconParser beaconParser = new BeaconParser()
.setBeaconLayout(BeaconParser.EDDYSTONE_URL_LAYOUT);
BeaconTransmitter beaconTransmitter = new BeaconTransmitter(getApplicationContext(),
beaconParser);
beaconTransmitter.startAdvertising(beacon);
} catch (MalformedURLException e) {
Log.d(TAG, "That URL cannot be parsed");
}
Full Disclosure: I am the lead developer on the Android Beacon Library open source project.
I am using Parse.com as my backend. I am trying to load an object in the background which works fine, but these objects have a pointer to an image. The problem is, that in order to query the images in the image class, i pass a ParseObject in order to get which object the image is pointing to like this:
ParseObject advertToGetImageFrom = ParseObject.createWithoutData("Advert", advertList.get(j).getObjectId());
However, my log shows this result
D/app: DT34J9zFKI
D/app: dAqVrnZ1rf
D/app: bNtIfOCqeE
D/app: Images loaded: 1
D/app: Images loaded: 1
D/app: Images loaded: 1
Which is reasonable since the loading is async. However, all the returned images are the same? Why is this happening? Here is the entire call:
for (int j = 0; j < titleMap.size(); j++){
ParseQuery<ParseObject> query2 = ParseQuery.getQuery("Image");
ParseObject advertToGetImageFrom = ParseObject.createWithoutData("Advert", objectList.get(j).getObjectId());
Log.d("app", advertToGetImageFrom.getObjectId());
query2.whereEqualTo("advertId", advertToGetImageFrom);
query2.findInBackground(new FindCallback<ParseObject>() {
public void done(final List<ParseObject> imageList, ParseException e) {
// commentList now has the comments for myPost
Log.d("app", "Images loaded: " + imageList.size());
if (imageList != null) {
ParseFile pFile = (ParseFile) imageList.get(0).get("image");
pFile.getDataInBackground(new GetDataCallback() {
public void done(byte[] data, ParseException e) {
if (e == null) {
Bitmap bmp = decodeFile(data);
ParseObject advertID = imageList.get(0).getParseObject("advertId");
imageMap.put(advertID.getObjectId(), bmp);
if (imageMap.size() == titleMap.size())
updateCardView();
} else {
Log.d("test", "There was a problem downloading the data.");
}
}
});
}
}
});
}
Not familliar with parse.com but I can hazard a guess.
I suspect your problem is with the line:
ParseObject advertID = imageList.get(0).getParseObject("advertId");
Here you are accessing the imageList that was provided to the done callback of your findInBackground invocation. The problem is that you are now in the done callback of a getDataInBackground invocation and although the imageList is final that does not make it immutable and has probably changed since the original call.
This doesn't seem like a terrible thing at first sight as this should only affect the advertID but think about that further and you may see the problem.
I think the solution could be to grab a local copy of the data that might change. Something like:
pFile.getDataInBackground(new GetDataCallback() {
// **** Grab the ParseObject befor it gets changed.
ParseObject theParseObject = imageList.get(0);
public void done(byte[] data, ParseException e) {
if (e == null) {
Bitmap bmp = decodeFile(data);
// **** Refer to the grabbed ParseObject not the changing outer one.
ParseObject advertID = theParseObject.getParseObject("advertId");
If that doesnt fix your issue then I feel certain it will be something like this.
I have a wicket page like this:
public final class Services extends WebPage {
public Services() {
super();
}
public Services(PageParameters params) {
StringValue serviceType = params.get("name");
if("report".equals(serviceType.toString())) {
this.getResponse().write("HelloWorld");
}
}
}
and it's html is empty.
when I call URL of this page I see this error
Index: 0, Size: 0
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:604)
at java.util.ArrayList.get(ArrayList.java:382)
at java.util.Collections$UnmodifiableList.get(Collections.java:1211)
at org.apache.wicket.markup.Markup.get(Markup.java:130)
at org.apache.wicket.Component.internalRender(Component.java:2356)
at org.apache.wicket.Component.render(Component.java:2307)
at org.apache.wicket.Page.renderPage(Page.java:1010)
at org.apache.wicket.request.handler.render.WebPageRenderer.renderPage(WebPageRenderer.java:121)
at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:271)
at org.apache.wicket.core.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:165)
at org.apache.wicket.request.cycle.RequestCycle$HandlerExecutor.respond(RequestCycle.java:861)
at org.apache.wicket.request.RequestHandlerStack.execute(RequestHandlerStack.java:64)
at org.apache.wicket.request.cycle.RequestCycle.execute(RequestCycle.java:261)
at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:218)
at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:289)
at org.apache.wicket.protocol.http.WicketFilter.processRequestCycle(WicketFilter.java:259)
at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:201)
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:282)
...
i don't know really what is wrong in my simple wicket page.
Wicket ever expects some markup in the associated HTML template to the code. The line that is the originator of your Exception is in Component.class see
private final void internalRender()
{
...
MarkupElement elem = markup.get(0);
...
}
That code gets a 'root' element of the associated markup. A code without any markup is a non-sence in Wicket.
TIP: If you just want to handle a request, mount a shareable resource or some of other resources other then a page.