i have implemented a webview in my android app and trying to highlight or to mark element when user click in the layout.
The webview is initialized as following :
myWebView.getSettings().setJavaScriptEnabled(true);
//myWebView.getSettings().setGeolocationEnabled(true);
//myWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
myWebView.getSettings().setBuiltInZoomControls(true);
myWebView.getSettings().setDomStorageEnabled(true);
myWebView.setWebViewClient(new WebViewController());
Trying to mark the element which is clicked by user for example like in this screenshot :
Selection with dot
I'm getting all the page divs via jsoup :
doc = Jsoup.connect(url).get();
final Elements alldivs = doc.select("div");
ArrayList<String> list = new ArrayList<String>();
for (org.jsoup.nodes.Element e : alldivs) {
if (!e.id().equals(""))
list.add(e.id());
}
But how to mark the selection as the photo above, and after that select marked content from div id.
How can make some thing like this ?
I'm using this javascript into webview to hightlight the selection but how to get the clicked element programmatically like : id of selected div or other values
public class MyWebViewClient extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript: "
+ "Object.prototype.each = function (fn, bind) {\n" +
" console.log(bind);\n" +
" for (var i = 0; i < this.length; i++) {\n" +
" if (i in this) {\n" +
" fn.call(bind, this[i], i, this);\n" +
" }\n" +
" }\n" +
" };\n" +
"\n" +
" var _addListener = document.addEventListener || document.attachEvent,\n" +
" _eventClick = window.addEventListener ? 'click' : 'onclick';\n" +
"\n" +
" var elements = document.getElementsByTagName(\"div\");\n" +
"\n" +
" elements.each(function (el) {\n" +
" _addListener.call(el, _eventClick, function () {\n" +
// todo process the clicked div element
" el.style.cssText = \"border-color: black;border-style: dashed;\"\n" +
" }, false);\n" +
" })");
}
}
If I understand correctly, you want to get some information from the HTML component into the native side.
According to this answer, it is not possible to pass arbitrary objects to Java, but at least you can pass the HTML code of the clicked node and then parse it natively.
This code based on yours does exactly that.
MainActivity.java: I guess this is pretty self-explanatory. The only thing I did different from you is to get the Javascript code from a separate file, so it's easier to maintain.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WebView myWebView = findViewById(R.id.webView);
final WebSettings settings = myWebView.getSettings();
settings.setJavaScriptEnabled(true);
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
final String injectedJs = "javascript:(function(){" + injectedJs() + "})()";
myWebView.loadUrl(injectedJs);
}
});
myWebView.addJavascriptInterface(
new Object() {
#JavascriptInterface
public void onClick(String param) {
Toast.makeText(MainActivity.this, param, Toast.LENGTH_LONG).show();
}
},
"appHost"
);
myWebView.loadUrl("https://google.com");
}
// Javascript code to inject on the Web page
private String injectedJs() {
BufferedReader stream = null;
StringBuilder jsBuilder = new StringBuilder();
try {
stream = new BufferedReader(new InputStreamReader(getAssets().open("js.js")));
String line;
while ((line = stream.readLine()) != null) {
jsBuilder.append(line.trim());
}
return jsBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
}
js.js: The base of this part is your code. Keep in mind that since injectedJs() removes all line markers, every statement needs to finish, including comments, hence the /*...*/ instead of //
/* Keep track of the last clicked element so we can un-highlight it */
var lastSelectedItem = null;
var showHighlighted = function(/* HTML element */view, /*boolean */highlighted) {
if (view) {
view.style.cssText = highlighted? 'border-color: black;border-style: dashed;' : '';
}
};
/* This new method, _addEventListener and _eventClick are the same as yours */
Object.prototype.each = function (fn, bind) {
for (var i = 0; i < this.length; i++) {
if (i in this) {
fn.call(bind, this[i], i, this);
}
}
};
var _addListener = document.addEventListener || document.attachEvent,
_eventClick = window.addEventListener ? 'click' : 'onclick';
/* I changed the element selection criteria, but you can change it back easily.
I am adding event listeners all the leaf elements in the DOM. */
var elements = document.body.getElementsByTagName('*');
elements.each(function (el) {
if (el.children.length == 0) {
_addListener.call(el, _eventClick, function () {
/* First, deal with the previously selected item*/
showHighlighted(lastSelectedItem, false);
if (lastSelectedItem !== null) {
appHost.onClick(lastSelectedItem.outerHTML);
}
/* Update the selected item reference */
lastSelectedItem = el;
/* Finally, deal with the previously selected item*/
showHighlighted(lastSelectedItem, true);
appHost.onClick(el.outerHTML);
}, false);
}
});
consider the web view as a view of a web page. You need to configure the element inside that web view to send a request that would fire an intent in your android application, which is possible, but it would not work for multiple users unless you know the user in that web view and authenticate the users... the point is , it is very complicated if you want to send that request from web to the logical part of your app. Even if you can do it, it is not optimal and i discourage it.
On the other hand, what you can do if you insist on using web views is to complete the rest of your logical operations on the web view. Don't go back from the web view to the app java logic.
Normally web views are used to show something rather than to make actions on the app. (the action might be used on the same web view)
I hope you do get , I've tried to explain as much as possible.
Related
A short and simple question someone hopefully has an awnser to:
How can I navigate with the Here Android SDK Premium through road elemts that have the attributes DIR_NO_CARS, NO_THROUGH_TRAFFIC, DIR_NO_TRUCKS in the TRUCK transport mode? Like I am a special car and I can drive on these roads.
My code looks like the following:
public class Scratch extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AndroidXMapFragment mapFragment = (AndroidXMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapfragment);
boolean success = com.here.android.mpa.common.MapSettings.setIsolatedDiskCacheRootPath(
getApplicationContext().getExternalFilesDir(null) + File.separator + ".here-maps",
"MainActivity");
System.out.println(success);
mapFragment.init(new OnEngineInitListener() {
#Override
public void onEngineInitializationCompleted(
OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// now the map is ready to be used
Map map = mapFragment.getMap();
for (String sheme : map.getMapSchemes()) {
Log.d("Custom", sheme);
}
map.setMapScheme("pedestrian.day");
map.setMapDisplayLanguage(Locale.GERMANY);
//Show current position marker
PositioningManager.getInstance().start(PositioningManager.LocationMethod.GPS_NETWORK);
mapFragment.getPositionIndicator().setVisible(true);
//Routing
GeoCoordinate start = new GeoCoordinate(50.992189, 10.999966);
GeoCoordinate target = new GeoCoordinate(51.001224, 10.990920);
//Start - End marker for routing
MapLabeledMarker markerStart = new MapLabeledMarker(start)
.setLabelText(map.getMapDisplayLanguage(), "Start")
.setIcon(IconCategory.ALL);
markerStart.setZIndex(12);
map.addMapObject(markerStart);
MapLabeledMarker markerTarget = new MapLabeledMarker(target)
.setLabelText(map.getMapDisplayLanguage(), "End")
.setIcon(IconCategory.ALL);
markerTarget.setZIndex(12);
map.addMapObject(markerTarget);
CoreRouter router = new CoreRouter();
router.setDynamicPenalty(NewPenaltyForStreetArea(
router.getDynamicPenalty(),
new GeoCoordinate(51.001137, 10.989901),
new GeoCoordinate(50.992582, 10.999338),
map.getMapDisplayLanguage(),
"Im Geströdig",
DrivingDirection.DIR_BOTH,
70
));
RouteOptions routeOptions = new RouteOptions();
routeOptions.setTransportMode(RouteOptions.TransportMode.TRUCK);
routeOptions.setRouteType(RouteOptions.Type.FASTEST);
routeOptions.setCarpoolAllowed(false);
routeOptions.setCarShuttleTrainsAllowed(false);
routeOptions.setDirtRoadsAllowed(true);
routeOptions.setTruckLength(6.590f);
routeOptions.setTruckWidth(2.150f);
routeOptions.setTruckHeight(2.150f);
routeOptions.setTruckTrailersCount(0);
routeOptions.setTruckDifficultTurnsAllowed(true);
routeOptions.setRouteCount(2);
RoutePlan routePlan = new RoutePlan();
routePlan.setRouteOptions(routeOptions);
routePlan.addWaypoint(new RouteWaypoint(start));
routePlan.addWaypoint(new RouteWaypoint(target));
class RouteListener implements CoreRouter.Listener {
// Method defined in Listener
public void onProgress(int percentage) {
// Display a message indicating calculation progress
Log.d("Custom", percentage + "");
}
// Method defined in Listener
#Override
public void onCalculateRouteFinished(List<RouteResult> routeResult, RoutingError error) {
// If the route was calculated successfully
if (error == RoutingError.NONE) {
// Render the route on the map
Log.d("Custom", routeResult.size() + " Routes calculated");
for (RouteResult result : routeResult) {
MapRoute mapRoute = new MapRoute(result.getRoute());
mapRoute.setColor(Color.argb(100, 201, 42, 42));
mapRoute.setZIndex(10);
if (routeResult.indexOf(result) == 0) {
//Best route
mapRoute.setColor(Color.argb(255, 201, 42, 42));
mapRoute.setZIndex(11);
}
map.addMapObject(mapRoute);
}
}
else {
// Display a message indicating route calculation failure
}
}
}
router.calculateRoute(routePlan, new RouteListener());
} else {
System.out.println("ERROR: Cannot initialize AndroidXMapFragment");
System.out.println(error);
}
}
});
}
private DynamicPenalty NewPenaltyForStreetArea(DynamicPenalty dynamicPenalty, GeoCoordinate cord1, GeoCoordinate cord2, String marcCode, String streetName, DrivingDirection drivingDirection, int speed){
List<GeoCoordinate> penaltyArea = new ArrayList<>();
penaltyArea.add(cord1);
penaltyArea.add(cord2);
List<RoadElement> roadElements = RoadElement.getRoadElements(GeoBoundingBox.getBoundingBoxContainingGeoCoordinates(penaltyArea), marcCode);
for (int i = 0; i < roadElements.size(); i++) {
//Log.d("Custom", roadElements.get(i).getRoadName());
if (!roadElements.get(i).getRoadName().equals(streetName)){
roadElements.remove(i);
i--;
}
else
Log.d("Custom", roadElements.get(i).getAttributes().toString());
}
Log.d("Custom", "Set penalty for " + roadElements.size() + " road elements - " + streetName);
for (RoadElement road : roadElements) {
dynamicPenalty.addRoadPenalty(road, drivingDirection, speed);
}
return dynamicPenalty;
}
}
And this is what I get
But this is what I need
So I want to say the navigation API that the road "Im Geströdig" is accessible for my car.
Road Element Attributes I need to change somehow:
[DIR_NO_CARS, DIRT_ROAD, NO_THROUGH_TRAFFIC, DIR_NO_TRUCKS]
The solution to the use case is not trivial. The functionality of updating Road Element attributes is available via the HERE Custom Route API, where you would need to upload an overlay with a shape, that matches the road you want to modify. The attributes which can be updated are also limited. ("VEHICLE_TYPES":"49" indicates road is open for Cars, Truck, Pedestrian)
GET http://cre.api.here.com/2/overlays/upload.json
?map_name=OVERLAYBLOCKROAD
&overlay_spec=[{"op":"override","shape":[[50.10765,8.68774],[50.10914,8.68771]],"layer":"LINK_ATTRIBUTE_FCN","data":{"VEHICLE_TYPES":"49"}}]
&storage=readonly
&app_id={YOUR_APP_ID}
&app_code={YOUR_APP_CODE}
Make sure to use the same AppId, Appcode as being used with HERE Premium Mobile SDK.
Now this overlay can be used in HERE Premium Mobile SDK with FTCRRouter (still Beta feature)
FTCRRouter ftcrRoute = new FTCRRouter();
FTCRRouter.RequestParameters parmaters =new
FTCRRouter.RequestParameters(routePlan,"OVERLAYBLOCKROAD",true);
ftcrRoute.calculateRoute(parmaters, new FTCRRouter.Listener() {
#Override
public void onCalculateRouteFinished(List<FTCRRoute> list,
FTCRRouter.ErrorResponse errorResponse) {
if (errorResponse.getErrorCode() == RoutingError.NONE) {
List<GeoCoordinate> shape = list.get(0).getGeometry();
MapPolyline polyline = new MapPolyline();
polyline.setGeoPolyline(new GeoPolygon(shape));
polyline.setLineWidth(10);
m_map.addMapObject(polyline);
}else{
// Error
}
}
});
As the FTCRRouter is still in Beta, there are some limitation like Dynamic Penanlity is not supported and also the FTCRRouter always prefers to take the roads available in HERE Map data and uses the Roads from the overlay if necessary.
Hello to all Android Programmers, is there any way to search a specific word in an HTML Webview? Or lets say when you put a word in search box and search it and then it will automatically scroll to the word or highlight it but the text is not in TextView the text are formatted using WebView? Sure it may be possible when you store a paragraph in the database and it can be searched easily but the problem on it is it isn't formatted like HTML so is this possible?
Please see the image on this link
For Highlighting search word:
titleWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
if (searchText != null && !searchText.equals("")) {
int i = titleWebView.findAll(searchText);
Toast.makeText(getApplicationContext(), "Found " + i + " results !",
Toast.LENGTH_SHORT).show();
try {
Method m = WebView.class.getMethod("setFindIsUp", Boolean.TYPE);
m.invoke(titleWebView, true);
} catch (Throwable ignored) {
}
searchText = "";
}
}
});
You can achieve it by reading source of page in webview.
Inside
Add this method in onPageFinished() of webviewclient:
webView.loadUrl("javascript:window.HTMLOUT.processHTML('<html>'+" +
"document.getElementsByTagName('html')[0].innerHTML+'</html>');");
Declaration for above called method processHTML() which helps to
process source html to String/JSON Obj.
#SuppressWarnings("unused")
#JavascriptInterface
public String processHTML(final String html)
{
// Log.i("processed html",html);
Thread OauthFetcher=new Thread(new Runnable() {
#Override
public void run() {
htmlString= Html.fromHtml(html).toString();
// Log.i("htmlString",htmlString);
Gson gson = new Gson();
CustomPojoObject obj = gson.fromJson(htmlString, CustomPojoObject.class);
}
});OauthFetcher.start();
return htmlString;
}
My problem is simple but I have no clue how to solve it. I have a feedbackPanel and I want to show an error message if the BootstrapDownloadLink fails. With a submit I could easily do:
protected void onSubmit(AjaxRequestTarget target) {
...
error("File_not_found"); //Wicket will print this on the feedback panel
target.add(getModalPanel().getFeedbackPanel()); //But i need to refresh it first
}
But the button is inside a panel which I fill with a populateItem and is the only way I know to insert Bootstrap Styles to it. The code of the button:
BootstrapDownloadLink downloadDocument = new BootstrapDownloadLink(IDITEMREPEATER, file) {
#Override
public void onClick() {
File file = (File)getModelObject();
if(file.exists()) {
IResourceStream resourceStream = new FileResourceStream(new org.apache.wicket.util.file.File(file));
getRequestCycle().scheduleRequestHandlerAfterCurrent(new ResourceStreamRequestHandler(resourceStream, file.getName()));
} else {
error(getString("error_fichero_no_existe"));
/// ???? need to refresh-> getModalPanel().getFeedbackPanel()
}
}
};
downloadDocument.setIconType(GlyphIconType.clouddownload);
downloadDocument.add(new AttributeModifier("title", getString("documentos.descargar")));
downloadDocument.add(new AttributeModifier("class", " btn btn-info negrita btn-xs center-block"));
downloadDocument.setVisible(Boolean.TRUE);
list.add(downloadDocument);
You could create or extend from an AjaxDownloadLink, for example like here.
The main idea is to have an AjaxBehavior that does the download, and you get a public void onClick(AjaxRequestTarget target) in which you can add the FeedbackPanel
downloadBehavior = new AbstractAjaxBehavior()
{
private static final long serialVersionUID = 3472918725573624819L;
#Override
public void onRequest()
{
[...]
ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(
AjaxDownloadLink.this.getModelObject(), name);
handler.setContentDisposition(ContentDisposition.ATTACHMENT);
getComponent().getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
}
};
And use that behavior in the onclick:
#Override
public void onClick(AjaxRequestTarget aTarget)
{
String url = downloadBehavior.getCallbackUrl().toString();
if (addAntiCache) {
url = url + (url.contains("?") ? "&" : "?");
url = url + "antiCache=" + System.currentTimeMillis();
}
// the timeout is needed to let Wicket release the channel
aTarget.appendJavaScript("setTimeout(\"window.location.href='" + url + "'\", 100);");
}
You can use target.addChildren(getPage(), IFeedback.class). This will add all instances of IFeedback interface in the page to the AjaxRequestTarget.
You can also use FeedbackPanel.class instead of the interface.
Or use getPage().visit(new IVisitor() {...}) to find a specific feedback panel if you don't want to add others which are not related.
I am having an issue using chrome 56 with chromedriver 2.27 on mac 10.12.3, though I have tried several different versions of the driver and several different versions of the browser, and had the same issue. In the UI, I can see very clearly that a blocking layer has cleared, but selenium still thinks it has not. After waiting for the blocking layer to clear (just looking at the browser), if I capture the results of driver.getPageSource(), I get the 'old' page source, not the new page source (with the blocking layer removed). When i view the old page source as an html doc, I can see the blocking layer. When I view a screen shot taken when the test ends, there is clearly no blocking layer, and a manual inspection of the DOM shows that the element has been removed. Somehow selenium seems to be caching the old page source and not recognizing when an element has been removed from the DOM. I can't seem to force it to refresh the cached(?) html without completely reloading the page. I want to avoid re-loadng the page, because that invalidates a test of whether a blocking layer is being dropped correctly.
I have tried getting some information from with the blocking layer element (using something benign like element.isDisplayed(), or element.getLocation()), and these still seem to behave as if the element were still present.
Any suggestions on how to deal with this would be appreciated.
If your goal is to click the item irrespective of the status of blocking layer, you can use inline javascript to click an element. Sample code in below.
try {
e.click();
} catch (org.openqa.selenium.WebDriverException E1) {
((JavascriptExecutor) driver).executeScript("arguments[0].click();", e.findElements(By.xpath(".//a")).get(0));
}
I had the same issue running Selenium test with GWT popup panel.
Sometimes the animation won't finish, the popup and the glass behind stay on the DOM.
When that happen, I tried to remove from the dom these 2 elements, but got weird results (other elements from the dom were removed instead !!?).
At the end I implemented these functions that hide these 2 elements and change the className so that they won't bother us anymore (see code below)
waitForPopupOpeningAnimationFinished() : is to be launched after a click that is supposed to open a popup
waitForPopupClosingAnimationFinished() : is to be launched after a click on a popup button should close the popup
// 200 is the animation time of the popup, we wait a lot more, see com.google.gwt.user.client.ui.PopupPanel.ANIMATION_DURATION
public static final int POPUP_ANIMATION_WAIT_TIME = 300;
public static final String CLASS_GWT_DIALOG_BOX = "gwt-DialogBox";
public static final String X_POPUP = "//div[#class='" + CLASS_GWT_DIALOG_BOX + "']";
public static final By BY_POPUP = By.xpath(X_POPUP);
public static final String X_POPUP_TOTALLY_OPENED_LOCATOR = "//div[contains(#class, '" + CLASS_GWT_DIALOG_BOX + "') and contains(#style, 'clip: rect(auto, auto, auto, auto);')]";
public static final By BY_POPUP_TOTALLY_OPENED_LOCATOR = By.xpath(X_POPUP_TOTALLY_OPENED_LOCATOR);
private static final String CLASS_GWT_POPUP_PANEL_GLASS = "gwt-PopupPanelGlass";
private static final By BY_GLASS_PANEL = By.xpath("//div[contains(#class, '" + CLASS_GWT_POPUP_PANEL_GLASS + "')]");
public void waitForPopupOpeningAnimationFinished() {
LOGGER.info("waitForPopupOpeningAnimationFinished");
try {
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.visibilityOfElementLocated(BY_POPUP_TOTALLY_OPENED_LOCATOR));
} catch (TimeoutException e) {
LOGGER.info("Forcing popup to be visible");
try {
js.executeScript("var elementsToBeRemoved = document.getElementsByClassName('" + CLASS_GWT_DIALOG_BOX + "');" //
+ "if(elementsToBeRemoved.length>0){" //
+ "elementsToBeRemoved[0].style.overflow='visible';" //
+ "elementsToBeRemoved[0].style.clip='rect(auto,auto,auto,auto)';" //
+ "}");
} catch (JavascriptException e2) {
LOGGER.warn("Could not find popup to force to be open", e2.getCause());
}
}
}
public void waitForPopupClosingAnimationFinished() {
try {
LOGGER.info("waitForPopupClosingAnimationFinished");
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_POPUP));
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_GLASS_PANEL));
} catch (TimeoutException e) {
LOGGER.info("Waiting for popup closing did not work as expected, forcing it");
javascriptRemovalOfElementByClassName(CLASS_GWT_DIALOG_BOX);
javascriptRemovalOfElementByClassName(CLASS_GWT_POPUP_PANEL_GLASS);
}
}
private void javascriptRemovalOfElementByClassName(String className) {
LOGGER.info("javascriptRemovalOfElementByClassName : {}", className);
try {
js.executeScript("var elementsToBeRemoved = document.getElementsByClassName('" + className + "');" //
+ "if(elementsToBeRemoved.length>0){" //
+ "elementsToBeRemoved[0].style.visibility = 'hidden';" //
+ "elementsToBeRemoved[0].style.display = 'none';" //
+ "elementsToBeRemoved[0].className='shouldHaveBeenRemovedFromDom';" //
+ "}");
LOGGER.info("Successfully removal of element with class {}", className);
} catch (JavascriptException jse) {
LOGGER.info("Apparently element with class {}} does not exists, we are good to go : {}", className, jse.toString());
}
}
public void waitForPopupClosingAnimationFinished() {
try {
LOGGER.info("waitForPopupClosingAnimationFinished");
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_POPUP));
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_GLASS_PANEL));
} catch (TimeoutException e) {
LOGGER.info("Waiting for popup closing did not work as expected, forcing it");
javascriptRemovalOfElementByClassName(CLASS_GWT_DIALOG_BOX);
javascriptRemovalOfElementByClassName(CLASS_GWT_POPUP_PANEL_GLASS);
}
}
I also had the same kind of issue with expanding/collapsing TreeNode animation so I implemented the below functions to be launched after a node expand/collapse:
private static final String X_EXPANDING_TREE_NODE =
"//div[contains(#role, 'treeitem') and #aria-expanded='true']/div[contains(#style, 'overflow: hidden;') and contains(#style, 'height:') and contains(#style, 'position: relative;')]/div[contains(#style, 'top:') and contains(#style, 'position: relative;')]";
private static final By BY_EXPANDING_TREE_NODE = By.xpath(X_EXPANDING_TREE_NODE);
private static final String X_COLLAPSING_TREE_NODE =
"//div[contains(#role, 'treeitem') and #aria-expanded='false']/following:div[contains(#style, 'overflow: hidden;') and contains(#style, 'height:') and contains(#style, 'position: relative;')]/div[contains(#style, 'top:') and contains(#style, 'position: relative;')]";
private static final By BY_COLLAPSING_TREE_NODE = By.xpath(X_COLLAPSING_TREE_NODE);
/**
* When a report tree node is expanding or collapsing is loading, we can see stuff like
* <div style="overflow: hidden; height: 1px; position: relative;">
* <div style="top: -122px; position: relative;">
* <p>
* We wait for it do disappear
* <p>
* or we force it to animation finished like this for expanding:
*
* <div style="overflow: hidden;">
* <div style="">
*
* or we force it to animation finished like this for collapsing:
*
* <div style="overflow: hidden; display : none;">
* <div style="">
*/
public void waitForTreeNodeExpandedAndCollapsed() {
waitForTreeNodeExpanded();
waitForTreeNodeCollapsed();
}
public void waitForTreeNodeExpanded(){
waitForTreeNode(true);
}
public void waitForTreeNodeCollapsed(){
waitForTreeNode(false);
}
private void waitForTreeNode(boolean expanding) {
try {
waiter.withTimeout(Duration.ofSeconds(2)).until(ExpectedConditions.invisibilityOfElementLocated(expanding?BY_EXPANDING_TREE_NODE:BY_COLLAPSING_TREE_NODE ));
} catch (TimeoutException e) {
boolean noJavascriptException = false;
while (!noJavascriptException) { // While javascript throw error like InvalidStateError, we continue
try {
LOGGER.info("Waiting for tree expand or collapse did not work as expected, forcing it");
js.executeScript("while(true){" //
+ "var iterator = document.evaluate(\"" + (expanding?X_EXPANDING_TREE_NODE:X_COLLAPSING_TREE_NODE) + "\",document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null );" //
+ "var thisNode = iterator.iterateNext();" //
+ "if(thisNode) {" //
+ "thisNode.parentNode.style='overflow: hidden;" + (expanding?"":"display: none;") + "';"//
+ "thisNode.style='';"
+ "thisNode = iterator.iterateNext();" //
+ "}else {" //
+ "break;" //
+ "}" //
+ "}");
noJavascriptException = true;
} catch (Exception e2) {
LOGGER.info("Javascript pb", e2.getCause());
}
}
}
}
I would like to get back results in a variable of type String. The contents of what the user tells his smartphone via Google now that is: "OK google claptrap " I would like to do this so I could get back 'claptrap'. I have already searched but have been unsuccessful. I found how to return my selectable application in Google now like, for example write a note in my app but in this case the person has to say " OK google note at me claptrap " so that I can get back claptrap. It is not proceeding well … I am pretty sure that it is possible because the app "commandr" already makes it for commands as "turn on the torch".
Excuse my bad english.
Thank you in advance Good evening :D
i have create a AccessibilityService for get command google now but i should touch editText for receive the command. Help me please
public class NotificationService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
System.out.println("******onAccessibilityEvent*******");
if(event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED) {
System.out.println(" NAME : " + event.getClassName());
System.out.println(" NAME PCK : " + event.getPackageName());
System.out.println(" SOURCE : " + event.getSource());
System.out.println(" TEXT : " + event.getText());
}
}
private String RecupCommandGoogle(AccessibilityEvent mEvent, AccessibilityNodeInfo mSource) {
if (mSource != null & mEvent.getClassName().equals("android.view.View")) {
return String.valueOf(mSource.performAction(AccessibilityNodeInfo.ACTION_SELECT));
}
return null;
}
#Override
protected void onServiceConnected() {
System.out.println("onServiceConnected");
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOWS_CHANGED | AccessibilityEvent.TYPE_VIEW_FOCUSED ;
info.packageNames = new String[] {"com.google.android.launcher" , "com.google.android.googlequicksearchbox"};
info.feedbackType = AccessibilityEvent.TYPES_ALL_MASK;
info.notificationTimeout = 100;
setServiceInfo(info);
}
#Override
public void onInterrupt() {
System.out.println("onInterrupt");
}
}