I combined learnings from two different sources to detect open windows using UIAutomation and to move the windows:
Detect the opening of a new window in C#
Move a UI Automation Element
I can get practically every app window to move (Win32 apps and UWP apps). Except for the Microsoft Edge browser window!
QUESTION: Does anyone know how to use UIAutomation to move the Edge window? Does anyone know what is special about the Edge browser that prevents it from adhering to Microsoft's own UIAutomation library?
Here's my full console app code (required: Add references to UIAutomationClient and UIAutomationTypes):
using System;
using System.Threading;
using System.Windows.Automation;
namespace UiAutomationTest
{
class Program
{
static void Main(string[] args)
{
Automation.AddAutomationEventHandler(eventId: WindowPattern.WindowOpenedEvent, element: AutomationElement.RootElement, scope: TreeScope.Children, eventHandler: OnWindowOpened);
Console.ReadLine();
Automation.RemoveAllEventHandlers();
}
private static void OnWindowOpened(object sender, AutomationEventArgs e)
{
try
{
var element = sender as AutomationElement;
if (element != null)
{
var hWnd = new IntPtr(element.Current.NativeWindowHandle);
Console.WriteLine($"Opened: {element.Current.Name} (Pid:{element.Current.ProcessId}),hWnd:{Convert.ToInt32(hWnd.ToString())}");
var _windowPattern = GetControlPattern(element, WindowPattern.Pattern) as WindowPattern;
if (_windowPattern == null)
{
Console.WriteLine("WindowPattern is null! Aborting.");
return;
}
if (false == _windowPattern.WaitForInputIdle(10000))
{
Feedback("Object not responding in a timely manner.");
return;
}
Console.WriteLine("Window is ready for input");
var _transformPattern = GetControlPattern(element, TransformPattern.Pattern) as TransformPattern;
if (_transformPattern == null)
{
Console.WriteLine("TransformPattern is null! Aborting.");
return;
}
// Is the TransformPattern object moveable?
if (_transformPattern.Current.CanMove)
{
Console.WriteLine("Waiting 3 seconds before move...");
// Wait a bit
Thread.Sleep(3000);
// Move element
_transformPattern.Move(250, 500);
Console.WriteLine("Window was moved!");
}
else
{
Feedback("Window is not moveable.");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType()}: {ex.Message}\n{ex.StackTrace}");
}
}
/// <summary>
/// Gets a specified control pattern.
/// </summary>
/// <param name="ae">The automation element we want to obtain the control pattern from.</param>
/// <param name="ap">The control pattern of interest.</param>
/// <returns>A ControlPattern object.</returns>
private static object GetControlPattern(AutomationElement ae, AutomationPattern ap)
{
if (false == ae.TryGetCurrentPattern(ap, out object oPattern))
{
Feedback("Object does not support the " + ap.ProgrammaticName + " Pattern");
return null;
}
Feedback("Object supports the " + ap.ProgrammaticName + " Pattern.");
return oPattern;
}
private static void Feedback(string message)
{
Console.WriteLine(message);
}
}
}
Here's a video showing that I can open Internet Explorer and move it, Chrome and move it, but Edge won't move!
CantMoveEdgeWindow.mp4
System Details:
Windows 10 1809
Visual Studio 2019 16.7
.NET Framework 4.7.2 C# Console App
Lastly, I have even tried using old school Win32 APIs such as EnumWindows, MoveWindow, SetWindowPlacement, and SetWindowPos. The Edge browser appears to be housed inside of an ApplicationFrameHost.exe process window. When I try to move the window, I get the same result as using the UIAutomation libraries: It "says" it passed, but the window doesn't actually move!
Related
I have a fundamental problem that I can not really understand where the problem comes from.
I am designing a project by xamarin.android webview.
Now I need to run a Java function in Web View and check the return value in a if function.
I searched all the websites and in all of them I got the following code:
In Main Activity Class:
public class MainActivity : AppCompatActivity
{
WebView web_view;
.
.
.
Define web_view public in class
In OnCreate :
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
web_view = FindViewById<WebView>(Resource.Id.webview);
web_view.Settings.JavaScriptEnabled = true;
web_view.Settings.BuiltInZoomControls = true;
web_view.Settings.AllowContentAccess = true;
web_view.SetWebViewClient(new HelloWebViewClient());
web_view.LoadUrl("https://www.example.com");
}
In Back Key Press:
public override bool OnKeyDown(Android.Views.Keycode keyCode, Android.Views.KeyEvent e)
{
if (keyCode == Keycode.Back)
{
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat)
{
JavascriptResult jsr = new JavascriptResult();
string strjs = "closePackageDetails();";
web_view.EvaluateJavascript(strjs, jsr);
string rrr = jsr.strResult;
Toast.MakeText(this, "message send:" + rrr, ToastLength.Long).Show();
}
else
{
Toast.MakeText(this, "android version less 4.4" , ToastLength.Long).Show();
}
}
}
On JavascriptResult Class (separate on C# Class Palaced in MainActivity in Root directory)
namespace webviewapp
{
class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string strResult;
public void OnReceiveValue(Java.Lang.Object result)
{
Toast.MakeText(Android.App.Application.Context, "رسیدن نتیجه احضار شد", ToastLength.Long);
strResult = ((Java.Lang.String)result).ToString();
}
}
}
<>
Everything looks right and the program is debugged without error and the APK file is created successfully.
After installing the program on the mobile phone and running it, the web page is loaded and everything looks good.
By touching the back button, the JavaScript function is executed correctly and the result is visible in Web View. But the result, which is a boolean value, is not returned.
In fact, the OnReceiveValue procedure does not work.
The variable 'rrr' always displays an null value.
Where it went wrong really puzzled me?
It is happening because the callback is executed later than the next line ,so the variable 'rrr' is always null.
Add the breakpoint at OnReceiveValue and the line string rrr = jsr.strResult; to check it .
Just do the next thing directly in the method OnReceiveValue in the callback class.
There doesn't appear to be a lot of people using Xamarin for Visual Studio consequently there isn't a lot of information specific to that platform out there.
Having said that, I've been trying to get a Floating Action Button (FAB) to work and it's been quite the exercise. I finally got it to appear and assign it to a variable in the activity with help from the nice folks who use StackOverflow, but cannot get the android:onClick="FabOnClick" call to work. Clicking on the FAB causes the app to crash with the error:
Unhandled Exception:
Java.Lang.IllegalStateException: Could not find method FabOnClick(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.design.widget.FloatingActionButton with id 'fab' occurred
This is the code in my activity:
public void FabOnClick(View v)
{
int x = 1;
}
It doesn't really do anything because I'm just trying to capture the click event for now. I set a breakpoint on the int x = 1 line to see when it's is executed. So what am I missing?
* Update *
I updated my activity code based on #Digitalsa1nt's answer below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Acr.UserDialogs;
using Android.Net;
using System.Net;
using Android.Support.Design.Widget;
using System.Threading.Tasks;
using Android.Views.InputMethods;
using static Android.Views.View;
namespace OML_Android
{
[Activity(Label = "CreateAccount")]
public class CreateAccount : Activity
{
public string result = "";
public EditText aTextboxUsername;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.CreateAccount);
RequestedOrientation = Android.Content.PM.ScreenOrientation.Portrait;
aTextboxUsername = FindViewById<EditText>(Resource.Id.aTextboxUsername);
EditText aTextboxPassword = FindViewById<EditText>(Resource.Id.aTextboxPassword);
EditText aTextboxPassword2 = FindViewById<EditText>(Resource.Id.aTextboxPassword2);
EditText txtEmailAddress = FindViewById<EditText>(Resource.Id.txtEmailAddress);
EditText txtEmailAddress2 = FindViewById<EditText>(Resource.Id.txtEmailAddress2);
EditText txtFirstName = FindViewById<EditText>(Resource.Id.first_name);
EditText txtMI = FindViewById<EditText>(Resource.Id.mi);
EditText txtLastName = FindViewById<EditText>(Resource.Id.last_name);
EditText txtAddress = FindViewById<EditText>(Resource.Id.address);
EditText txtCity = FindViewById<EditText>(Resource.Id.city);
Spinner spnState = FindViewById<Spinner>(Resource.Id.state);
EditText txtZip = FindViewById<EditText>(Resource.Id.zip);
MaskedEditText.MaskedEditText txtPhone = FindViewById<MaskedEditText.MaskedEditText>(Resource.Id.phone);
Spinner spnCompany = FindViewById<Spinner>(Resource.Id.company_spinner);
Spinner spnDept = FindViewById<Spinner>(Resource.Id.department_spinner);
Spinner spnSection = FindViewById<Spinner>(Resource.Id.section_spinner);
Button ButtonSubmit = FindViewById<Button>(Resource.Id.button_submit);
ScrollView sv = FindViewById<ScrollView>(Resource.Id.scrollView1);
ButtonSubmit.SetBackgroundColor(Android.Graphics.Color.YellowGreen);
// Hide the keyboard (also doesn't work)
InputMethodManager board = (InputMethodManager)GetSystemService(Context.InputMethodService);
board.HideSoftInputFromWindow(aTextboxUsername.WindowToken, 0);
// get the floating action button.
FloatingActionButton myFab = FindViewById< FloatingActionButton>(Resource.Id.fab);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
DataInterfaceWeb.DataInterface myService = new DataInterfaceWeb.DataInterface();
myFab.Click += FabButton_Click(); // <-- get error here
try
{
ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
NetworkInfo activeConnection = connectivityManager.ActiveNetworkInfo;
bool isOnline = (activeConnection != null) && activeConnection.IsConnected;
if (!isOnline)
{
showMessage("There is no internet or cell phone connection. Connect to a network or connect to a cellular network.", "ERROR");
}
}
catch (Exception ex)
{
showMessage("Connectivity Manager failed to create a connection due to error: " + ex.Message, "ERROR");
};
// Create your application here
ButtonSubmit.Click += async (sender, e) =>
{
try
{
result = myService.CheckForUser(Master.username, Master.password, aTextboxUsername.Text);
if (result.ToUpper() == "Y")
{
await showMessage("Username " + aTextboxUsername.Text + " is already in use. Please choose another", "ERROR");
// aTextboxUsername.SetSelectAllOnFocus(true);
aTextboxUsername.RequestFocus();
View insideView = FindViewById<EditText>(Resource.Id.aTextboxUsername);
sv.ScrollTo(0, (int)insideView.GetY());
aTextboxUsername.SelectAll();
}
}
catch (Exception ex)
{
showMessage("Account creation attempt failed due to error: " + ex.Message, "ERROR");
}
};
}
public async Task showMessage(string message, string messageType)
{
var result = await UserDialogs.Instance.ConfirmAsync(new ConfirmConfig
{
Message = messageType + System.Environment.NewLine + message,
OkText = "Ok",
});
}
public void FabButton_Click()
{
int x = 1;
}
}
}
The error I get now is:
Cannot implicitly convert 'void' to 'SystemEventHandler' on the line myFab.Click += FabButton_Click();.
#Digitalsa1nt did point me in the right direction. Instead of
fabButton.Click += FabButton_Click;
I just wired up an event, as the error suggested (duh):
myFab.Click += (sender, e) =>
{
FabButton_Click();
};
It now works as I would expect.
So I'm making a couple of assumptions in this answer. Firstly that you are working with a Xamarin.Native project and not a Xamarin.Forms project.
Secondly I am assuming you are using the FloatingActionButton from one of the support libraries such as: Android.Support.Design.Widget (base / V4 / V7).
Once you've defined your FAB within the AXML Layout page:
<android.support.design.widget.FloatingActionButton
app:backgroundTint="#color/colourPrimary"
android:id="#+id/fabButton"
android:src="#drawable/image"
app:fabSize="normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:elevation="16dp"
android:translationZ="12dp"
app:rippleColor="#ffa9a9a9" />
You can get it from within your activity as such:
using Android.Support.Design.Widget;
// declare variable
private FloatingActionButton fabButton;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// call base
base.OnCreateView(inflater, container, savedInstanceState);
// inflate our view
var view = inflater.Inflate(Resource.Layout.MainTabWishlistPage, container, false);
// get our instance of the button using the resource ID.
fabButton = view.FindViewById<FloatingActionButton>(Resource.Id.fabButton);
// assign to click event
fabButton.Click += FabButton_Click;
}
private void FabButton_Click(object sender, EventArgs e)
{
int x = 1;
}
The above example is based on it being a fragment rather than an activity, but the methodology is the same.
Official Git Repo:
Xamarin/monodroid-samples - Floating Action Button Basic
Random online guide:
android-material-design-floating-action
In case this is a Xamarin.Forms project, look into James Montemagno's library (p.s one of the developers that works on Xamarin and creates tons of libraries to help make your life easier, definitely look through his other repos.)
jamesmontemagno/FloatingActionButton-for-Xamarin.Android
I'm trying to emulate normal browser behaviour in my vaadin grid, which includes middle mouse click to open in a new tab:
addItemClickListener(e -> {
boolean newTab = e.getMouseEventDetails().getButton() == MouseEventDetails.MouseButton.MIDDLE || e.getMouseEventDetails().isCtrlKey();
//open in window or new tab
});
However, the middle mouse button is not registered by vaadin. How could I get this to work?
That feature was included in vaadin-grid (which goes into Vaadin 10) and will not work in Vaadin 8.
For Vaadin 8, you can either intercept the event with some client-side extension, or use a ComponentRenderer for adding a Panel to each component (which works, but is not ideal because it degrades performance):
grid.addColumn(item->{
Panel p = new Panel(item.getName());
p.setStyleName(ValoTheme.PANEL_BORDERLESS);
p.addClickListener(ev->{
System.out.println(ev.getButtonName());
});
return p;
}).setRenderer(new ComponentRenderer());
A client-side extension, on the other hand, allows listening to javascript events (such as MouseEvent) and triggering a server event in response. Creating a extension is quite a complex topic (since it uses a part of the API that is normally hidden from the developer) but it allows direct access to rendered DOM, which is not possible otherwise.
The following resources from the documentation may give you a starting point:
Creating a component extension (which describes a simple extension with Java code only) and Integrating JavaScript Components and Extension (which explains how to add native JavaScript code to your extension).
How I solved the problem in my specific case:
Server side:
public class MyGrid<T> extends Grid<T> {
public MyGrid(String caption, DataProvider<T, ?> dataProvider) {
super(caption, dataProvider);
MiddleClickExtension.extend(this);
}
public static class MiddleClickExtension<T> extends AbstractGridExtension<T> {
private MiddleClickExtension(MyGrid<T> grid) {
super.extend(grid);
registerRpc((rowKey, columnInternalId, details) -> grid.fireEvent(
new ItemClick<>(grid, grid.getColumnByInternalId(columnInternalId), grid.getDataCommunicator().getKeyMapper().get(rowKey), details)),
MiddleClickGridExtensionConnector.Rpc.class);
}
public static void extend(MyGrid<?> grid) {
new MiddleClickExtension<>(grid);
}
#Override
public void generateData(Object item, JsonObject jsonObject) {
}
#Override
public void destroyData(Object item) {
}
#Override
public void destroyAllData() {
}
#Override
public void refreshData(Object item) {
}
}
}
Client side:
#Connect(MyGrid.MiddleClickExtension.class)
public class MiddleClickGridExtensionConnector extends AbstractExtensionConnector {
#Override
protected void extend(ServerConnector target) {
getParent().getWidget().addDomHandler(event -> {
if (event.getNativeButton() == NativeEvent.BUTTON_MIDDLE) {
event.preventDefault();
CellReference<JsonObject> cell = getParent().getWidget().getEventCell();
getRpcProxy(Rpc.class).middleClick(cell.getRow().getString(DataCommunicatorConstants.KEY), getParent().getColumnId(cell.getColumn()),
MouseEventDetailsBuilder.buildMouseEventDetails(event.getNativeEvent(), event.getRelativeElement()));
}
}, MouseDownEvent.getType());
}
#Override
public GridConnector getParent() {
return (GridConnector) super.getParent();
}
public interface Rpc extends ServerRpc {
void middleClick(String rowKey, String columnInternalId, MouseEventDetails details);
}
}
Is there a method for creating a log file using a blackberry api ?
Something like log4j ?
I'd like to save this log file on pc running the emulator, is this possible ?
At Antair, during development of our BlackBerry applications, we often include a debug console in the dev builds of our apps.
With the debug console, all debug output hits the output screen when the dev build is running in the simulator, and when the dev build runs on a physical test device, the debug output is automatically persisted and is available to view on a dedicated screen that can be pulled up via a menu option or button. With a little code modification, you can easily have the debug log be rerouted to a file, emailed or sent over a network connection.
The code below is a stripped-down version of the debug console we use at our company.
Using the console is easy. Include the code in your project, fill out the PERSISTENCE_GUID for your application, set the TAGID string to identify your application name in the debug logs, and when you want to output a debug statement, simply call Debug.print(”Something happened here…“);
Each line of the debug output, both in the output window when running in a simulator, and in the debug console screen when viewed on a device, will contain your debug message, the thread number on which the call was made (useful for thread/ui debugging), and the date/time of the log statement, with a millisecond timestamp for performance profiling.
To view the debug console on a real device, simple put in a call to pushScreen(new AntairLogScreen()). The screen has a built-in menu item to clear the persisted log messages, and will dismiss itself like a regular application screen.
If you’re running the RIM compiler preprocessor to switch between development, QA, and production builds, you can simply put in a call to set Debug.ENABLED = false for everything but the development builds, and the debug console will be there when you need to debug and go away quietly when you don’t need it.
The code is below.
// ---------------------------------------------------------------------------
// Antair Debug Log (for the BlackBerry API)
// http://www.antair.com
// ---------------------------------------------------------------------------
package com.antair.examples.debug;
import net.rim.device.api.i18n.SimpleDateFormat;
import java.util.Date;
import net.rim.device.api.collection.util.BigVector;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.RichTextField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.Persistable;
final class Debug implements Persistable
{
final static boolean ENABLED = true;
final static String TAGID = "MY_PROJECT";
final static long PERSISTENCE_GUID = /* YOUR OWN PERSISTENCE GUID */;
private BigVector _messages = new BigVector();
static String print(String str)
{
if ( Debug.ENABLED )
{
StringBuffer sb = new StringBuffer();
sb.append(TAGID);
sb.append("\n");
sb.append(Thread.currentThread().toString());
sb.append("\n");
sb.append(new SimpleDateFormat("MM/dd/yy HH:mm:ss:SSS").format(
new Date()));
sb.append("\n");
sb.append(str); sb.append("\n");
str = sb.toString();
System.out.println(str);
System.out.flush();
Debug d = load();
d._messages.addElement(str);
save(d);
}
return str;
}
static BigVector getPersistedMessages()
{
return load()._messages;
}
static void clearPersistedMessages()
{
save(new Debug());
}
private static Debug load()
{
Debug d = null;
try
{
PersistentObject po =
PersistentStore.getPersistentObject(Debug.PERSISTENCE_GUID);
synchronized(po)
{
Object obj = po.getContents();
d = (obj == null) ? new Debug() : (Debug)obj;
}
}
catch ( Exception e )
{
d = new Debug();
}
return d;
}
private static void save(Debug d)
{
try
{
PersistentObject po =
PersistentStore.getPersistentObject(Debug.PERSISTENCE_GUID);
synchronized(po)
{
po.setContents(d);
po.commit();
}
}
catch ( Exception e )
{
}
}
}
final class ClearAntairLogScreenMenuItem extends MenuItem
{
ClearAntairLogScreenMenuItem(int position)
{
super("Clear Log", position, 0);
}
public void run()
{
Debug.clearPersistedMessages();
}
}
final class AntairLogScreen extends MainScreen
{
AntairLogScreen()
{
super(MainScreen.DEFAULT_CLOSE|MainScreen.DEFAULT_MENU);
StringBuffer text = new StringBuffer();
BigVector logItems = Debug.getPersistedMessages();
for ( int i = 0 ; i < logItems.size() ; ++i )
{
text.append((String)logItems.elementAt(i) + "\n");
}
add(new RichTextField(text.toString()));
}
protected void makeMenu ( Menu menu, int instance )
{
menu.add(new ClearAntairLogScreenMenuItem(100000));
}
}
BlackBerry has its own log facility - EventLogger, but I find it to be ugly. You can write to EventLogger, but you can not view it as easily as you might with other logs or for example you can not extract the content programmatically to send over http/email.
I think it is much more comfortable to use your custom logger class that writes log entries directly to a log file on the SD card. Since the SD card on the emulator is a folder on your PC, you can easily monitor the app with any PC log viewer in real time.
Also as a bonus with such approach you can have some code to send the log over http/email. Such logging on a real device will slow down the app significantly, so it should not be normally used in production, but your app could have such option (to enable the debug mose) so it'll allow to know the reason which otherwise would remain unknown.
hie i am using jde 4.5 want to use camera through my app.
i write the code and getting runtime excepetion Pushmodelscreen caaled by non event thread
tell me what the problem in it?
public void start Camera()
{
try {
// Create a player for the Blackberry's camera
Player player= Manager.createPlayer( "capture://video" );
// Set the player to the REALIZED state (see Player javadoc)
player.realize();
// Grab the video control and set it to the current display
_videoControl = (VideoControl)player.getControl( "VideoControl" );
if (_videoControl != null)
{
// Create the video field as a GUI primitive (as opposed to a
// direct video, which can only be used on platforms with
// LCDUI support.)
_videoField = (Field) _videoControl.initDisplayMode (VideoControl.USE_GUI_PRIMITIVE, "net.rim.device.api.ui.Field");
_videoControl.setDisplayFullScreen(true);
_videoControl.setVisible(true);
}
player.start();
if(_videoField!=null)
{
add(_videoField);
}
} catch (Exception e) {
// TODO: handle exception
Dialog.alert(e.getMessage());
}
}
`
thnaks alot
Amit
The code that involved in UI changes should be called from within the UI thread. So most likely some part of your code should be called in a way:
UIApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// do your UI related staff here
// e.g. push a Screen or call Dialog.alert(), etc.
}
});
Also, you may find this info interesting.