I can display the information of my phone application but I am restricted to display only six.
I would like to know how to proceed to display the rest of the elements ( i have a list of 17 elements).
For the moment I use the first and last columns of my Template to do an invalidate() and display the previous or next items of my list
public Template onGetTemplate() {
ListTemplate.Builder templateBuilder = new ListTemplate.Builder();
ItemList.Builder sectionABuilder = new ItemList.Builder();
if(compteur < 4 ) {
try {
if (DataMgr.getInstance().getDataListAdress().size() > 0 && compteur < DataMgr.getInstance().getDataListAdress().get(0).length) {
for (int i = compteur; i < compteur + 5; i++) {
System.out.println("////Test + " + DataMgr.getInstance().getDataListAdress().get(0)[i]);
System.out.println("////TestLength + " + DataMgr.getInstance().getDataListAdress().get(0).length);
sectionABuilder.addItem(buildRow(DataMgr.getInstance().getDataListAdress().get(0)[i]));
}
}
sectionABuilder.addItem(buildRowClick("Suivant"));
templateBuilder.addSectionedList(
SectionedItemList.create(sectionABuilder.build(), "Header"));
} catch (Exception e) {
CarToast.makeText(getCarContext(), "No more", CarToast.LENGTH_SHORT).show();
}
} else {
sectionABuilder.addItem(buildRowClickPrecedent("Precedent"));
try {
if (DataMgr.getInstance().getDataListAdress().size() > 0 && compteur < DataMgr.getInstance().getDataListAdress().get(0).length) {
for (int i = compteur; i < compteur + 4; i++) {
System.out.println("////Test + " + DataMgr.getInstance().getDataListAdress().get(0)[i]);
System.out.println("////Test +" + DataMgr.getInstance().getDataListAdress().get(0).length);
sectionABuilder.addItem(buildRow(DataMgr.getInstance().getDataListAdress().get(0)[i]));
}
}
sectionABuilder.addItem(buildRowClick("Suivant"));
templateBuilder.addSectionedList(
SectionedItemList.create(sectionABuilder.build(), "Header"));
} catch (Exception e) {
CarToast.makeText(getCarContext(), "No more", CarToast.LENGTH_SHORT).show();
}
}
return templateBuilder
.setHeaderAction(Action.PAN)
.setTitle("ok")
.build();
}
#NonNull
private Row buildRow(String data) {
return new Row.Builder()
.setTitle(data)
.build();
}
#NonNull
private Row buildRowClick(String data) {
return new Row.Builder()
.setOnClickListener(new OnClickListener() {
#Override
public void onClick() {
compteur += 4;
invalidate();
}
})
.setTitle(data)
.build();
}
You can't use it like this. Use ConstraintManager.class.
Lists are limited by unit you have in a car. But there is special API to handle this.
Check ConstraintManager:
int listLimit = Math.min(MAX_LIST_ITEMS,
getCarContext().getCarService(ConstraintManager.class).getContentLimit(
ConstraintManager.CONTENT_LIMIT_TYPE_LIST));
Docs: https://developer.android.com/reference/androidx/car/app/constraints/ConstraintManager
You can take this limit count and interate your list only based on this number.
I am using kotlin in my android app. I wrote following function for decoding coordinates :
private fun decodeCoordinates(address: String) {
var lat: String = ""
var lng: String = ""
var flag = true
var count = 0
while (count < address.length) {
if (address[count].equals(',')) {
flag = false;
continue
}
if (flag) {
lat += address[count]
} else {
lng += address[count]
}
count++;
}
Log.i("dxdiag", "Lat: $lat")
Log.i("dxdiag", "Lng: $lng")
}
but app hangs, if I remove continue , app does not hang
Issue is that you are changing the length of list inside the loop which leads to ANR. Use follinwg loop code:
var length = address.legth
while (count < length) {
if (address[count].equals(',')) {
flag = false;
continue
}
if (flag) {
lat += address[count]
} else {
lng += address[count]
}
count++;
}
I have developed an app, sort of an text reader, and need to introduce a new functionality to it (users are asking for it- I've intended to incorporate it all along, just that I never figured out how to do it). I'm generating an html from the text and displaying it in a webview. I want the user to be able to select a section of the text and then open the context menu. What I want to identify is on which section he has longClicked on (each line of the html consists of pre-formatted line/section number, the line text and may be finished with a href link), and which part of it was selected. Once I figure the first part out, the second is just a matter of sorting it out.
I've tried using <span id='someuniquenumber'> for the line text, but webView does not recognize it as an anchor*. I've also, unsuccessfully, tried to enable text selection on links (which IMHO is not a desirable option, but will resort to it, if there are no other options left).
Here's an example of the text selection problem- I want to be able to identify the selection as "line 5, words 4 through 10", or at least that the user has selected the text from the line marked as 5.
Any help would be greatly appreciated- just bear in mind that I can affect both the webView behaviour and the HTML code displayed. Thanks in advance. :)
event handler returns get extra=0 in type='null'
If I use tags, I do not know the way to allow text selection within that link, whereas without it I cannot identify the element user has clicked on (and the selected text may certainly not be unique, and therefore may not be searched within HTML).
EDIT
Here's the code I have regarding webView (onCreate in MainActivity):
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
int temp = url.lastIndexOf("/") + 1;
String link = url.substring(temp);
if (link.charAt(0) == 'L') {
link = link.substring(1);
LinksFragment mLinksFragment = LinksFragment.newInstance(Integer.valueOf(link),textZoom,zoomAll,sans,fragNo);
mLinksFragment.show(fm,"fragment_links");
} else if (link.charAt(0) == 'C') {
// show or add comment
} else {
// follow link
// append link to clipboard
ClipData tmp = myCB.getPrimaryClip();
if (!myCB.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
tmp = ClipData.newPlainText("text","");
myCB.setPrimaryClip(tmp);
}
ClipData.Item tmpI = tmp.getItemAt(0);
String ts = tmpI.getText().toString();
ts += link + " ";
tmp = ClipData.newPlainText("text",ts);
myCB.setPrimaryClip(tmp);
}
return true;
}
});
// detect clicked element
mWebView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
WebView.HitTestResult hr = ((WebView)view).getHitTestResult();
Toast.makeText(MainActivity.this, "getExtra = "+ hr.getExtra() + "Type= " + hr.getType(),
Toast.LENGTH_LONG).show();
//return true;
return false;
}
});
mWebView.getSettings().setDefaultFontSize(14);
mWebView.setBackgroundColor(0x00000000);
mWebView.getSettings().setBuiltInZoomControls(true);
mWebView.getSettings().setDisplayZoomControls(false);
updateFields(); // applies custom fontface and fontsize to elements incl. mWebView
mWebView.loadDataWithBaseURL("file:///android_asset/",wV,"text/html; charset=utf-8","utf-8",null);
And here's the code for generating html inserted into the webView (a separate function fetching text, adding headings and chapter titles, formatting the text)-the actual text is stored in tmpStr variable:
public String fetchText(Bible mB, boolean append, boolean filter, String wV) {
BibleInfo.Error mE;
String tN;
char tT;
int[] Lines;
String[] Lttrs;
int[] noVerses = new int[3];
String fS1 = "<p class='ps'>";
String Psalm = getResources().getString(R.string.Psalm);
String Chapter = getResources().getString(R.string.Chapter);
boolean estExc = false;
char lttr = 'a'-1;
mE = mB.mBI.mE;
int tmp = 0;
for (int i=0; i<3; i++) {
tmp += mB.mBI.noBooks(i);
noVerses[i] = mB.mBI.getLine(tmp,1,1);
}
// separating link data
int cnt = 0;
Lines = new int[mB.mLink.getLines().length];
Lttrs = new String[mB.mLink.getLines().length];
for (String tS:mB.mLink.getLines()) {
tN = "";
Lttrs[cnt] = "";
for (tmp=0; tmp<tS.length(); tmp++) {
tT = tS.charAt(tmp);
if (tT>='0' && tT<='9') {
tN += tT;
} else {
Lttrs[cnt] += tT;
}
}
Lines[cnt++] = Integer.valueOf(tN);
}
if (!append) {
wV = "";
}
noVerses = mB.mBI.getTriLink(Lines[0],mE);
if ((noVerses[1] == 0 || (noVerses[1] == 1 && noVerses[0] != mB.mBI.getSirach())) && mB.mLink.isLong()) {
if (mB.mBI.getTitles() != null) {
wV += "<h1>" + mB.mBI.getTitles()[noVerses[0]] + "</h1>";
}
}
for (int i=0; i<Lines.length; i++) {
int tX = Lines[i];
int[] temp = mB.mBI.getTriLink(tX, mE);
if (temp[2] == 1 && mB.mLink.isLong() && temp[1] != 0) {
if (temp[0] == mB.mBI.getPsalms()) {
wV += "<h2>" + Psalm + " " + temp[1] + "</h2>";
} else {
wV += "<h2>" + Chapter + " " + temp[1] + "</h2>";
}
}
String tmpStr = mB.getLineText(tX - 1,filter);
if (noVerses[0] == mB.mBI.getPsalms()) {
wV += fS1;
if (Lttrs[i] != "") {
tmpStr = parseVerse(tmpStr,Lttrs[i]);
}
tmpStr = tmpStr.replace(mB.mBI.mSeparator, "<br>");
} else {
wV += "<p>";
if (noVerses[0] == mB.mBI.getEsther()) {
int noBrks = 0;
int lastOccurrence = 0;
if ((temp[1]==1) & (temp[2]==1)) {
estExc = true;
while (lastOccurrence != -1){
lastOccurrence = tmpStr.indexOf(mB.mBI.mSeparator,lastOccurrence);
String tStr = "<br><sup>" + temp[2] + (char)(98+noBrks) + "</sup> ";
if (lastOccurrence != -1) {
if (noBrks == 0) {
tmpStr = "<span id='apoch'>" + tmpStr;
}
tmpStr = tmpStr.replaceFirst("\\|", tStr);
noBrks +=1;
}
}
if (tmpStr.lastIndexOf(mB.mBI.mSeparator) != -1) {
tmpStr += "</span>";
}
// remove character before last </sup>
} else {
estExc = false;
while (lastOccurrence != -1){
lastOccurrence = tmpStr.indexOf(mB.mBI.mSeparator,lastOccurrence);
String tStr = "<br><sup>" + temp[2] + (char)(97+noBrks) + "</sup> ";
if (lastOccurrence != -1) {
if (noBrks == 0) {
tmpStr = "<span id='apoch'>" + tmpStr;
}
tmpStr = tmpStr.replaceFirst("\\|", tStr);
noBrks +=1;
}
}
if (noBrks != 0) {
tmpStr += "</span>";
}
tmpStr = tmpStr.replaceFirst("<span id='apoch'>","");
tmpStr = tmpStr.replaceFirst("<br>","<br><span id='apoch'>");
}
}
}
// add hyperlink for links
if (!TextUtils.isEmpty(mB.getLinks()[tX-1])) {
lttr++;
if (lttr>'z') { lttr = 'a'; }
String ts= "<sup><span id='links'><a href='L" + (tX-1) + "'>" + lttr;
ts += "</a></span></sup>";
tmpStr += ts;
}
if (estExc) {
wV += "<sup>" + temp[2] + "a</sup> " + tmpStr + "</p>";
} else {
wV += "<sup>" + temp[2] + "</sup> " + tmpStr + "</p>";
}
}
wV = fS2 + wV;
return wV;
}
Calls to log events or set user properties fired from within a WebView must be forwarded to native code before they can be sent to Google Analytics for Firebase.
Implement JavaScript handler
The first step in using Google Analytics for Firebase in a WebView is to create JavaScript functions to forward events and user properties to native code. The following example shows how to do this in a way that is compatible with both Android and iOS native code
function logEvent(name, params) {
if (!name) {
return;
}
if (window.AnalyticsWebInterface) {
// Call Android interface
window.AnalyticsWebInterface.logEvent(name, JSON.stringify(params));
} else if (window.webkit
&& window.webkit.messageHandlers
&& window.webkit.messageHandlers.firebase) {
// Call iOS interface
var message = {
command: 'logEvent',
name: name,
parameters: params
};
window.webkit.messageHandlers.firebase.postMessage(message);
} else {
// No Android or iOS interface found
console.log("No native APIs found.");
}
}
function setUserProperty(name, value) {
if (!name || !value) {
return;
}
if (window.AnalyticsWebInterface) {
// Call Android interface
window.AnalyticsWebInterface.setUserProperty(name, value);
} else if (window.webkit
&& window.webkit.messageHandlers
&& window.webkit.messageHandlers.firebase) {
// Call iOS interface
var message = {
command: 'setUserProperty',
name: name,
value: value
};
window.webkit.messageHandlers.firebase.postMessage(message);
} else {
// No Android or iOS interface found
console.log("No native APIs found.");
}
}
Implement native interface
public class AnalyticsWebInterface {
public static final String TAG = "AnalyticsWebInterface";
private FirebaseAnalytics mAnalytics;
public AnalyticsWebInterface(Context context) {
mAnalytics = FirebaseAnalytics.getInstance(context);
}
#JavascriptInterface
public void logEvent(String name, String jsonParams) {
LOGD("logEvent:" + name);
mAnalytics.logEvent(name, bundleFromJson(jsonParams));
}
#JavascriptInterface
public void setUserProperty(String name, String value) {
LOGD("setUserProperty:" + name);
mAnalytics.setUserProperty(name, value);
}
private void LOGD(String message) {
// Only log on debug builds, for privacy
if (BuildConfig.DEBUG) {
Log.d(TAG, message);
}
}
private Bundle bundleFromJson(String json) {
// ...
}
}
Once you have created the native interface, register it with your WebView so that it is visible to Javascript code running in the WebView:
// Only add the JavaScriptInterface on API version JELLY_BEAN_MR1 and above, due to
// security concerns, see link below for more information:
// https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mWebView.addJavascriptInterface(
new AnalyticsWebInterface(this), AnalyticsWebInterface.TAG);
} else {
Log.w(TAG, "Not adding JavaScriptInterface, API Version: " + Build.VERSION.SDK_INT);
}
Source: Firebase
In Cloud code, I create a function that get the users, and another function that add specific values to that user objects.
The problem is, that in Android we get the users objects, but without the specific values that was added.
When i copy the same code to the JS file in the client side, it's works perfect. I get all the objects with all the specific values that was added to them.
I attached the code
Someone know what the problem is?
Parse.Cloud.define("getAllPeople", function (request, response) {
var currentUser = request.user;
var numberToSkip = request.params.numberToSkip || 0;
var isMale = request.params.isMale;
var latitude = request.params.latitude || 32.08; //Tel Aviv
var longitude = request.params.longitude || 34.78;
var parseGeoPoint = new Parse.GeoPoint({latitude: latitude, longitude: longitude});
var User = Parse.Object.extend("User");
var queryObject = new Parse.Query(User);
queryObject.skip(numberToSkip).limit(100)
.equalTo('isCheckIn', true)
.equalTo('isMale', isMale)
//.notEqualTo('objectId', currentUser.id)
.near('location', parseGeoPoint)
.notEqualTo('checkInPlace', null)
.include('checkInPlace')
.find({
success: function (usersInCheckIn) {
checkUserSend(currentUser, usersInCheckIn).then(function (results) {
response.success(results);
});
},
error: function (error) {
console.error("Error: " + error.code + " " + error.message);
}
});
});
function checkUserSend(currentUser, usersInCheckIn) {
var Matching = Parse.Object.extend("Matching");
var queryObject = new Parse.Query(Matching);
var promise = new Parse.Promise();
queryObject
.equalTo('sendUser', currentUser)
.include('sendUser')
.find({
success: function (results) {
for (var i = 0; i < results.length; i++) {
var current = {};
current.receivedUser = results[i].get('receivedUser');
current.isMatching = results[i].get('isMatching');
current.isRejected = results[i].get('isRejected');
for (var j = 0; j < usersInCheckIn.length; j++) {
if (usersInCheckIn[j].id == current.receivedUser.id) {
usersInCheckIn[j].sendWink = true;
usersInCheckIn[j].isMatching = current.isMatching;
usersInCheckIn[j].isRejected = current.isRejected;
}
}
}
console.log(usersInCheckIn);
promise.resolve(usersInCheckIn);
},
error: function (error) {
console.error("Error: " + error.code + " " + error.message);
}
});
return promise;
}
Remove return statement and add response.success(usersInCheckIn); on sucess of queryObject find()
function checkUserSend(currentUser, usersInCheckIn) {
var Matching = Parse.Object.extend("Matching");
var queryObject = new Parse.Query(Matching);
var promise = new Parse.Promise();
queryObject
.equalTo('sendUser', currentUser)
.include('sendUser')
.find({
success: function (results) {
for (var i = 0; i < results.length; i++) {
var current = {};
current.receivedUser = results[i].get('receivedUser');
current.isMatching = results[i].get('isMatching');
current.isRejected = results[i].get('isRejected');
for (var j = 0; j < usersInCheckIn.length; j++) {
if (usersInCheckIn[j].id == current.receivedUser.id) {
usersInCheckIn[j].sendWink = true;
usersInCheckIn[j].isMatching = current.isMatching;
usersInCheckIn[j].isRejected = current.isRejected;
}
}
}
console.log(usersInCheckIn);
// promise.resolve(usersInCheckIn);
response.success(usersInCheckIn);
},
error: function (error) {
console.error("Error: " + error.code + " " + error.message);
}
});
}
I'm developing an app in FlashDevelop, using Haxe and OpenFl
When I test my app in flash target, it works fine. But when I compile for android, it comes up with this error during the compilation:
./src/ReaderView2.cpp: In member function 'virtual Void ReaderView2_obj::setZoom()':
./src/ReaderView2.cpp:653: error: base operand of '->' has non-pointer type 'String'
Build halted with errors (haxelib.exe).
...Which is obviously something to do with cpp, which I'm not really an expert.
Does any body know what the error means?
Here's the setZooom function: (the whole file is quite large)
public function setZoom()
{
hideOptions();
while (numChildren > 0)
{
Main.remove(getChildAt(0));
}
if (image != null) if (image.parent != null) image.parent.removeChild(image);
images = new Array();
field = new TextField();
var fieldFont = Assets.getFont("fonts/Kreon-Regular.ttf");
var format:TextFormat = new TextFormat(fieldFont.fontName, currentZoom, 0x4F4F4F);
format.align = TextFormatAlign.LEFT;
field.defaultTextFormat = format;
field.embedFonts = true;
field.text = fullText;
field.selectable = false;
field.wordWrap = true;
field.border = false;
field.autoSize = TextFieldAutoSize.LEFT;
field.width = displayWidth;
//field.x = 0;
//split string into words
var allParas:Array<String> = fullText.split("\r\n");
var words:Array<String>;
var fields:Array<TextField> = new Array();
var tempField:TextField = null;
var contentHeight:Float = displayHeight;
var wordI:Int;
var paraI:Int = 0;
var tempArr2:Array<String>;
while (paraI < allParas.length)
{
if (false) //check img tag
{
}
else //if para is words
{
wordI = 0;
words = allParas[paraI].split(" ");
while (wordI < words.length)
{
if (tempField == null || tempField.textHeight > contentHeight)
{
if (tempField != null) {
wordI--;
tempArr2 = tempField.text.toString().split(" ");
for (i in 0... tempArr2.length)
{
tempArr2.remove("");
}
tempArr2.pop();
tempField.text = tempArr2.join(" ");
}
tempField = new TextField();
tempField.defaultTextFormat = field.getTextFormat();
tempField.embedFonts = true;
tempField.text = "";
tempField.border = false;
tempField.selectable = false;
tempField.wordWrap = true;
tempField.autoSize = TextFieldAutoSize.LEFT;
tempField.width = displayWidth-2;
tempField.x = 0;
fields.push(tempField);
}
else
{
tempField.appendText(words[wordI] + (wordI == words.length - 1? "\n": " "));
wordI++;
}
}
}
paraI++;
}
var bd:BitmapData;
for (i in 0... fields.length)
{
bd = new BitmapData(Std.int(fields[i].width), Std.int(fields[i].height));
bd.draw(fields[i]);
images.push(new Bitmap(bd, PixelSnapping.AUTO, true));
}
//addChild(fields[0]);
images[0].x = 10;
addChild(images[0]);
currentPageInstance = images[0];
currentPage = 0;
drawScrollBar();
if (optionsBtn!=null)addChild(optionsBtn);
}
So apparently using the toString() funcion gives problems for a cpp target.