Use Android AccessibilityService with nativescript - nativescript

Currently I make myself familiar with nativescript and trying to figure out what you can do with it. One thing that I find very interesting and for which I am more interested in is how I can use native Android functions.
As challenge for understanding and learning I have picked out the possibility in apps fill in text fields with specific values like some password managers apps do. If I understand it right, it shouldn't big voodoo.
How, I found two examples. The first one is the autofill feature from the keepass2android app. Sources: https://keepass2android.codeplex.com/SourceControl/changeset/view/9ddc142d987880118be38ee1d69943f2809738d3#src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/autofill/AutoFillService.java
Other sample I found here: AccessibilityNodeInfo - send text
First quick and dirty I hack the app.js, find out how the android native functions works in nativescript. So I want trigger somehow the different events but only init is triggered (so far I also understand it also).
var applicationModule = require("application");
var platform = require("platform");
var context = applicationModule.android.context;
android.accessibilityservice.AccessibilityService;
var aa = android.accessibilityservice.AccessibilityService.extend({
init: function() {
console.log("init()");
},
onInit: function() {
console.log("onInit()");
},
create: function() {
console.log("create()");
},
onCreate: function() {
console.log("onCreate()");
},
destroy: function() {
console.log("destroy()");
},
onDestroy: function() {
console.log("onDestroy()");
},
accessibilityEvent: function(event) {
console.log("accessibilityEvent");
},
onAccessibilityEvent: function(event) {
console.log("onAccessibilityEvent");
},
interrupt: function() {
console.log("interrupt");
},
onInterrupt: function() {
console.log("interrupt");
},
serviceConnected: function() {
console.log("serviceConnected");
},
onServiceConnected: function() {
console.log("onServiceConnected");
}
});
var a = new aa();
Probably android.accessibilityservice.AccessibilityService must somehow be extended in a different way, get it working. But how?
It would be a big deal if the onAccessibilityEvent function would be triggered in nativescript.
Edit:
I made some progress. Found some other nativescript sources and samples and modified my code. Now:
var applicationModule = require("application");
var platform = require("platform");
var utils = require("utils/utils");
var context = android.content.Context;
var ad = utils.ad.getApplicationContext();
var aa = ad.getSystemService(context.ACCESSIBILITY_SERVICE);
But the way, I can register onAccessibilityEvent is currently not clear for me.

You need to register your service (the class from your first code block) similar to this example:
http://developer.android.com/training/accessibility/service.html
and place your logic in onAccessibilityEvent override.

Related

Ember js integrating with D3

I want to include a graphs which have 2-y axis with relevant 2 data sets in my ember project.
Since I am newbie to D3 js as well as ember js I do some googling and come up with npm packages for doing that like these ember-d3, ember-charts, ember-d3-helpers. etc..
But all of them seems to me there is a bit of learning curve.
My questions are, from using those kind of packages can I integrate and draw my graphs?
Or else can I use directly D3 without any npm plunging?
Are there any suitable way to integrate D3 in ember project?
The simplested solution I am currently using is to import your third part libraries using ember-cli-build.js (see code below)
/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
});
app.import('./bower_components/d3/d3.js');
app.import('./bower_components/c3-0.4.11/c3.js');
app.import('./bower_components/c3-0.4.11/c3.css');
return app.toTree();
};
The code above let you have global access to d3 and c3 (which means which lib can be direct accessed in your window object ).
C3 is a really simple and powerful reusable d3 chart library and I recommend to every new d3 users.
http://c3js.org/
After you have your libs ready, put your chart generate code inside the component's didInsertElement hook (below just a sample and you can make it better for sure)
didInsertElement() {
this._super(...arguments);
const chart = c3.generate({
data: {
columns: []
},
axis: {
x: {
type: 'category',
categories: [
]
}
},
zoom: {
enabled: true
},
legend: {
show: false
},
grid: {
y: {show: true}
}
});
this.set('globalChart.lineChart', chart);
},
You can read more about Ember dependencies management at,
https://guides.emberjs.com/v2.11.0/addons-and-dependencies/managing-dependencies/

Using Parse react query goes to local storage instead of Parse.com

I have an observe function in a react native app that is needed by the Parse react mixin:
var Parse = require('parse').Parse;
var ParseReact = require('parse-react');
var React = require('react-native');
var PollingPage = React.createClass({
mixins: [ParseReact.Mixin],
observe: function (props, state) {
var inOutQuery = (new Parse.Query('IsOut'));
return state.isLoading ? {inOuts: inOutQuery} : null;
},
//etc
package.json:
"dependencies": {
"react-native": "^0.12.0"
},
"devDependencies": {
"parse": "^1.6.7",
"parse-react": "^0.5.0"
}
however when this is run it goes off to look in local storage:
getItem: function getItem(path) {
return localStorage.getItem(path);
},
Not to sure what is going on here.
I'm following this tutorial:
http://www.raywenderlich.com/106369/integrating-parse-react-native-ios#comments
The error:
That because window.localStorage not exist in JavaScriptCore. localStorage is a part of Web APIs, but JavaScriptCore is just an javascript engine, not "web browser". You should use AsyncStorage instead of localStorage.
Please see this official doc for more information about RN js runtime.
[EDIT]
I think you used the wrong lib. From ParseReact:
As of version 1.6, the Parse JS SDK has a different build for React Native. If you're using Parse+React on React Native, you'll need to require the 'parse-react/react-native' package instead.
and you can also check \node_modules\parse\lib\react-native\, there are 3 difference storage implement: StorageController.browser.js, StorageController.default.js and StorageController.react-native.js. In StorageController.react-native.js, the getItem function look like that:
getItemAsync: function getItemAsync(path) {
var p = new _ParsePromise2['default']();
_reactNative.AsyncStorage.getItem(path, function (err, value) {
if (err) {
p.reject(err);
} else {
p.resolve(value);
}
});
return p;
},

getjson does not work on production server but works on development machine

I have following javascript that is using a selection changed to fill in a select list.
$(function () {
$("#bedrijvenauto").each(function () {
var target = $(this);
var dest = target.attr("data-autocomplete-destination");
target.autocomplete({
source: target.attr("data-autocomplete-source"),
select: function (event, ui) {
alert('selected bedrijf');
event.preventDefault();
target.val(ui.item.label);
$(dest).val(ui.item.value);
$("#projectenauto").val("");
alert('selected bedrijf');
alert($('#BEDRIJF_ID').val());
$.getJSON("/Project/GetListViaJson", { bedrijf: $('#BEDRIJF_ID').val() }, function (data) {
alert('selected bedrijf');
alert(data);
$("#PROJECT_ID").empty();
$("#PROJECT_ID").append(new Option("Maak een selectie", 0));
for (var i = 0; i < data.length; ++i) {
alert(data[i].value + ' ' + data[i].label);
$("#PROJECT_ID").append(new Option(data[i].label, data[i].value));
}
});
},
focus: function (event, ui) {
event.preventDefault();
target.val(ui.item.label);
}
});
target.val($("#BEDRIJF_NAAM").val());
});
It works like a charm on my development pc. The alert are all coming out even the data is returning results. That is the difference with the development pc that does not give any results after the call to getJSON
I have the feeling I am missing a detail here.
I am not used to debugging on a webserver because I usually create GUI applications in WPF, and this is a student's work for his vacation and I now got to get it working without him being around anymore. Vacation is done :-(
But not for me.
The 404 error indicated in your comments means the url your creating is incorrect. Always make use of the #Url.Action() method to ensure they are correctly generated. In your script
var url = '#Url.Action("GetListViaJson", "Project")';
$.getJSON(url, { bedrijf: $('#BEDRIJF_ID').val() }, function (data) {
....
}
or if this is an external script, then add the var url = '#Url.Action(...)'; in the main view (razor code is not evaluated in external script files), or add it as a data- attribute to the element your handling
data-url = "#Url.Action(...)"
and get it again using var url = $(someElement).data('url');

Routing in Extjs with DeftJs

deftjs looks really promising as it adds exactly the necessary things I missed in the MVC implementation of ExtJs.
What I actually miss is a functionality that makes routing possible/ easy. Extjs has a Ext.ux.Router functionality but I formerly used code like this with help of this lib:
initRoutes: function () {
var me = this;
Log.debug('Books.controller.App: initRoutes');
//use PATH.JS library until ExtJs supports routing as Sencha Touch 2.0 does. (see utils\Path)
Path.map("#/home").to(function () {
me.getController('Home').index();
});
Path.map("#/trackingsheet").to(function () {
me.getController('TrackingSheet').index();
});
Path.root('#/home');
Path.listen();
}
As the procedure of creating the crucial parts in deftjs is now exactly the other way around (view creates the controller) I certainly cannot refer to a controller's method and instantiate the view and make it the visible one. I have a pretty simple card layout here - what means only one view can be visible at a time, it is not necessary to go any deeper like this (e.g. make a task pane visible or the like).
What is the preferred way to do it?
I can think of making the Viewport a view factory having some methods like the controller before.
Thanks,
da5id
I solved this problem by using Ext.util.History class in a history context class that can raise an event when the hash changes:
Ext.define('myApp.context.HistoryContext', {
mixins: {
observable: 'Ext.util.Observable'
},
constructor: function(config) {
var me = this;
if (config == null) {
config = {};
}
this.initConfig(config);
Ext.util.History.add('home');
//init Ext.util.History; if there is a hash in the url,
//controller will fire the event
Ext.util.History.init(function(){
var hash = document.location.hash;
me.fireEvent('tokenChange', hash.replace('#', ''));
});
//add change handler for Ext.util.History; when a change in the token occurs,
//this will fire controller's event to load the appropriate content
Ext.util.History.on('change', function(token){
me.fireEvent('tokenChange', token);
});
this.mixins.observable.constructor.call(this);
this.addEvents('tokenChange');
return this.callParent(arguments);
}
});
Then you can inject this context in to your controller, and observe the token change, and implement the action in dispatch method:
Ext.define('myApp.controller.HomeController', {
extend: 'Deft.mvc.ViewController',
inject: [
'historyContext'
],
control: {
appContainer: {},
home: {
click: 'addHistory'
},
about: {
click: 'addHistory'
}
},
observe: {
historyContext: {
tokenChange: "dispatch"
}
},
init: function() {
return this.callParent(arguments);
},
switchView: function(view) {
//change this to get the cards for your card layout
this.getAppContainer().add(Ext.ComponentMgr.create({
xtype : view,
flex : 1
}));
},
addHistory: function(btn) {
var token = btn.itemId;
Ext.util.History.add(token);
},
dispatch: function(token) {
// switch on token to determine which content to load
switch(token) {
case 'home':
this.switchView('view-home-Index');
break;
case 'about':
this.switchView('view-about-Index');
break;
default:
break;
}
}
});
This should be ok for the first level routing (#home, #about), but you need to implement your own mechanism to fetch the token for the second and third level routes. (#home:tab1:subtab1) You can possibly create a service class that can handle fetching the hash and inject the service to each controllers to dispatch.
For further discussion in this topic, go to https://github.com/deftjs/DeftJS/issues/44

"sessionstore-state-read" observer not working

Im currently making a firefox extension that will let you choose what tabs you want to reopen after startup, instead of opening everything (it opens about:sessionrestore page). My JS code looks like this:
Edit: code updated with working version
chrome.manifest
component {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} components/myextension.js
contract #example.com/MyExtension;1 {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
category profile-after-change MyExtension #example.com/MyExtension;1
components/myextension.js
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const CI = Components.interfaces, CC = Components.classes, CR = Components.results;
// class declaration
function MyExtension () {}
MyExtension.prototype = {
classDescription: "My Firefox Extension",
classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
contractID: "#example.com/MyExtension;1",
QueryInterface: XPCOMUtils.generateQI([CI.nsIObserver]),
// add to category manager
_xpcom_categories: [{
category: "profile-after-change"
}],
observe: function(aSubject, aTopic, aData)
{
var obs = CC["#mozilla.org/observer-service;1"].getService(CI.nsIObserverService);
switch (aTopic)
{
case "profile-after-change":
obs.addObserver(this, "sessionstore-state-read", false);
break;
case "sessionstore-state-read":
aSubject.QueryInterface(CI.nsISupportsString);
let newData = { windows: [{ tabs: [{ entries: [{
url: "about:sessionrestore",
formdata: { "#sessionData": aSubject.data }
}] }] }] };
aSubject.data = JSON.stringify(newData);
break;
default:
throw Components.Exception("Unknown topic: " + aTopic);
}
}
};
if (XPCOMUtils.generateNSGetFactory)
var NSGetFactory = XPCOMUtils.generateNSGetFactory([EmbarrassedObserver]);
else
var NSGetModule = XPCOMUtils.generateNSGetModule([EmbarrassedObserver]);
It works fine with sessionstore-state-write (commented lines), but i want to do it with the sessionstore-state-read event, cuz i dont want to edit what it writes to the disk. The problem is, when i change to the read event, my observer stops working. My guess is i register the observer too late (after the session is read), so i tried to register it as soon as possible (when the extension is first loaded, insted of window load event), but it still wont work. Any idea how to get it working?
You need to write your extension as an XPCOM component that listens to the profile-after-change notification (the first notification that extensions are allowed to listen to). Then your component will be able to listen to the sessionstore-state-read notification.

Resources