I am using in my app Digital Persona SDK for fingerprint identification.
When i use the identify function on less then 250 fmds it works fine.
Engine.Candidate candidates[] = m_engine.Identify(searchedFmd, 0, fmdArray, DEFAULT_THRESHOLD, 1); //fmdArray < 250
But with fmdArray > 250 it gives me a native runtime error:
A/art: art/runtime/indirect_reference_table.cc:132] JNI ERROR (app bug): local reference table overflow (max=512)
Now i runned this app on couple of android devices and came to conclusion that my app crushes with fmdArray > 250 when its running on android 7. But android 8 works fine. In 8 i can preform a check on even 4000 fmds and it works fine.
But i need to run this code in a specific device, that running android 7.
I tried to run it in couple of threads of 250 fmds only. But after single run there is another problem with the SDK. On the second run it doesnt works.
This is what i do:
First i get a fingerprint capture that i want to identify:
Reader.CaptureResult capture = m_reader.Capture(fidFormat, UrUSDK.DefaultImageProcessing, m_DPI, timeout);
// In second run, code after this line is not executed.
// My guees its not coming back from native. No exeptions. No errors.
...
Fmd scannedFmd = m_engine.CreateFmd(capture.image, fmdFormat);
...
int index = identifyFinger(fmds, scannedFmd);
...
private int identifyFinger(List<Fmd> fmdSearchArray, Fmd scannedFmd) {
List<List<Fmd>> lists = splitToChunks(fmdSearchArray);
AtomicInteger index = new AtomicInteger(-1);
List<Callable<Void>> threads = new ArrayList<>(lists.size());
AtomicInteger iteratorIndex = new AtomicInteger(0);
for (int i = 0; i < lists.size(); i++) {
int currentChunk = i;
Callable<Void> thread = () -> {
System.out.println(Thread.currentThread().getName() + " with chunk: " + iteratorIndex.getAndIncrement());
Fmd[] fmds = lists.get(currentChunk).toArray(new Fmd[IDENTIFY_BOUNDARY]);
try {
Engine.Candidate[] candidates = m_engine.Identify(scannedFmd, 0, fmds, threshold, 1);
if (candidates.length > 0) {
index.set(candidates[0].fmd_index + (currentChunk * IDENTIFY_BOUNDARY));
}
} catch (UareUException e) {
}
System.out.println(Thread.currentThread().getName() + " with chunk: " + currentChunk + " finished!");
return null;
};
threads.add(thread);
}
try {
List<Future<Void>> futures = executorService.invokeAll(threads);
System.out.println("All threads finished: " + index.get());
return index.get();
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
}
}
...
private List<List<Fmd>> splitToChunks(List<Fmd> fmdSearchArray) {
int size = fmdSearchArray.size();
List<List<Fmd>> lists;
if (size > IDENTIFY_BOUNDARY) {
int chunks = size / IDENTIFY_BOUNDARY;
if (size % IDENTIFY_BOUNDARY > 0) {
chunks++;
}
lists = new ArrayList<>(chunks);
for (int i = 0; i < chunks; i++) {
if (i + 1 == chunks) {
lists.add(new ArrayList<>(fmdSearchArray.subList(i * IDENTIFY_BOUNDARY, size)));
break;
}
lists.add(new ArrayList<>(fmdSearchArray.subList(i * IDENTIFY_BOUNDARY, (i + 1) * IDENTIFY_BOUNDARY)));
}
} else {
lists = new ArrayList<>(1);
lists.add(fmdSearchArray);
}
return lists;
}
The problem with this code is that it runs once. But at another try it doesnt come back from the native code of Caprture call.
So my question is:
How i can overcome this and make it work from my java code?
Or at least what is the direction of the solution?
The root cause is that this Identify function holds on to at least two references per returned Candidate after pushing it to the result array. It should instead release the references after pushing, so its use of the (limited) local reference table remain constant. You should file a bug about that.
The simplest workaround for now is to cut your fmdArray into 250-sized chunks and call Identify for each chunk.
Related
I am trying to get the app code and display it, for an example if button X starts a new activity then a textView displays the whole method
I reached only how can I display code in HTML format from this question
But is there is a way to get the code of my app out, I think that there are 2 ways
An Internal one by getting it by the app itself
An External one by reading the java file then filtering it and getting the text of the method
Is there are any ideas about that?
Thanks in advance
The above is not currently possible as mentioned by others is the comments. What i can suggest is shipping your application with the source code in the assets folder and using a helper function to extract a certain methods from the source at runtime (your second proposed approach). I have written example code but it is in pure java and needs to be ported to android (a few lines).
NB: You may need to reformat the code after extraction depending on your use case.
Hope it helps :)
The code for the helper method:
static String getTheCode(String classname ,String methodSignature ) throws FileNotFoundException {
//**********************A few lines of code below need changing when porting ***********//
// open file, your will be in the assets folder not in the home dir of user, don't forget the .java extension when porting
File file = new File(System.getProperty("user.home") +"/"+ classname +".java");
// get the source, you can use FileInputReader or some reader supported by android
Scanner scanner = new Scanner(file);
String source = "";
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
//**********************The above code needs changing when porting **********//
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0 ,lastIndex);
}
Example usage (getTheCode methods is static and in a class called GetTheCode):
public static void main(String... s) throws FileNotFoundException {
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut()"));
System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut(String word)"));
}
Output:
private static void shoutOut(){ // nothing to here }
private static void shoutOut(String word){ // nothing to here }
NB: When starting your new activity create a method eg
private void myStartActivty(){
Intent intent = new Intent(MyActivity.this, AnotherActivity.class);
startActivity(intent);
}
Then in your onClick:
#Override
public void onClick(View v) {
myStartActivity();
myTextView.setText(GetTheCode.getTheCode("MyActivity","private void myStartActivity()"));
}
Update: Ported the Code for android:
import android.content.Context;
import java.io.IOException;
import java.util.Scanner;
public class GetTheCode {
static String getTheCode(Context context, String classname , String methodSignature ) {
Scanner scanner = null;
String source = "";
try {
scanner = new Scanner(context.getAssets().open(classname+".java"));
while(scanner.hasNext()) {
source += " "+ scanner.next();
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
scanner.close();
// extract code using the method signature
methodSignature = methodSignature.trim();
source = source.trim();
//appending { to differentiate from argument as it can be matched also if in the same file
methodSignature = methodSignature+"{";
//making sure we find what we are looking for
methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
methodSignature = methodSignature.replaceAll("\\s+", " ");
source =source.replaceAll("\\s*[(]\\s*", "(");
source = source.replaceAll("\\s*[)]\\s*", ")");
source = source.replaceAll("\\s*[,]\\s*", ",");
source = source.replaceAll("\\s+", " ");
if(!source.contains(methodSignature)) return null;
// trimming all text b4 method signature
source = source.substring(source.indexOf(methodSignature));
//getting last index, a methods ends when there are matching pairs of these {}
int lastIndex = 0;
int rightBraceCount = 0;
int leftBraceCount = 0;
char [] remainingSource = source.toCharArray();
for (int i = 0; i < remainingSource.length ; i++
) {
if(remainingSource[i] == '}'){
rightBraceCount++;
if(rightBraceCount == leftBraceCount){
lastIndex = (i + 1);
break;
}
}else if(remainingSource[i] == '{'){
leftBraceCount++;
}
}
return source.substring(0,lastIndex);
}
}
Usage:
// the method now takes in context as the first parameter, the line below was in an Activity
Log.d("tag",GetTheCode.getTheCode(this,"MapsActivity","protected void onCreate(Bundle savedInstanceState)"));
Let's start with a broader overview of the problem:
Display App code
Press X button
Open new activity with a textview which displays the method
The goal is to do the following:
Viewing app method by extracting it and then building & running it.
There are some methods we can use to run Java/Android code dynamically. The way I would personally do it is DexClassLoader and with Reflection.
If you need more details, let me know. Here is what it'd do though:
View app method
Upon pressing X, launch intent with extra to new Activity
Parse and compile code dynamically and then run it with DexClassLoader and Reflection
Sources:
Sample file loading Java method from TerminalIDE Android App
Android Library I made for Auto-Updating Android Applications without needing the Play Store on non-root devices
I'm building an android app using Xamarin. The requirement of the app is to capture video from the camera and encode the video to send it across to a server.
Initially, I was using an encoder library on the server-side to encode recorded video but it was proving to be extremely unreliable and inefficient especially for large-sized video files. I have posted my issues on another thread here
I then decided to encode the video on the client-side and then send it to the server. I've found encoding to be a bit complicated and there isn't much information available on how this can be done. So, I searched for the only way I knew how to encode a video that is by using FFmpeg codec. I've found some solutions. There's a project on GitHub that demonstrates how FFmpeg is used inside a Xamarin android project. However, running the solution doesn't give any output. The project has a binary FFmpeg file which is installed to the phone directory using the code below:
_ffmpegBin = InstallBinary(XamarinAndroidFFmpeg.Resource.Raw.ffmpeg, "ffmpeg", false);
Below is the example code for encoding video into a different set of outputs:
_workingDirectory = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
var sourceMp4 = "cat1.mp4";
var destinationPathAndFilename = System.IO.Path.Combine (_workingDirectory, "cat1_out.mp4");
var destinationPathAndFilename2 = System.IO.Path.Combine (_workingDirectory, "cat1_out2.mp4");
var destinationPathAndFilename4 = System.IO.Path.Combine (_workingDirectory, "cat1_out4.wav");
if (File.Exists (destinationPathAndFilename))
File.Delete (destinationPathAndFilename);
CreateSampleFile(Resource.Raw.cat1, _workingDirectory, sourceMp4);
var ffmpeg = new FFMpeg (this, _workingDirectory);
var sourceClip = new Clip (System.IO.Path.Combine(_workingDirectory, sourceMp4));
var result = ffmpeg.GetInfo (sourceClip);
var br = System.Environment.NewLine;
// There are callbacks based on Standard Output and Standard Error when ffmpeg binary is running as a process:
var onComplete = new MyCommand ((_) => {
RunOnUiThread(() =>_logView.Append("DONE!" + br + br));
});
var onMessage = new MyCommand ((message) => {
RunOnUiThread(() =>_logView.Append(message + br + br));
});
var callbacks = new FFMpegCallbacks (onComplete, onMessage);
// 1. The idea of this first test is to show that video editing is possible via FFmpeg:
// It results in a 150x150 movie that eventually zooms on a cat ear. This is desaturated, and there's a fade-in.
var filters = new List<VideoFilter> ();
filters.Add (new FadeVideoFilter ("in", 0, 100));
filters.Add(new CropVideoFilter("150","150","0","0"));
filters.Add(new ColorVideoFilter(1.0m, 1.0m, 0.0m, 0.5m, 1.0m, 1.0m, 1.0m, 1.0m));
var outputClip = new Clip (destinationPathAndFilename) { videoFilter = VideoFilter.Build (filters) };
outputClip.H264_CRF = "18"; // It's the quality coefficient for H264 - Default is 28. I think 18 is pretty good.
ffmpeg.ProcessVideo(sourceClip, outputClip, true, new FFMpegCallbacks(onComplete, onMessage));
//2. This is a similar version in command line only:
string[] cmds = new string[] {
"-y",
"-i",
sourceClip.path,
"-strict",
"-2",
"-vf",
"mp=eq2=1:1.68:0.3:1.25:1:0.96:1",
destinationPathAndFilename2,
"-acodec",
"copy",
};
ffmpeg.Execute (cmds, callbacks);
// 3. This lists codecs:
string[] cmds3 = new string[] {
"-codecs",
};
ffmpeg.Execute (cmds, callbacks);
// 4. This convers to WAV
// Note that the cat movie just has some silent house noise.
ffmpeg.ConvertToWaveAudio(sourceClip, destinationPathAndFilename4, 44100, 2, callbacks, true);
I have tried different commands but no output file is generated. I have tried to use another project found here but this one has the same issue. I don't get any errors but no output file is generated. I'm really hoping someone can help me find a way I can manage to use FFmpeg in my project or some way to compress video to transport it to the server.
I will really appreciate if someone can point me in the right direction.
Just figure how to get the output by adding the permission in AndroidManifest file.
android.permission.WRITE_EXTERNAL_STORAG
Please read the update on the repository, it says that there is a second package, Xamarin.Android.MP4Transcoder for Android 6.0 onwards.
Install NuGet https://www.nuget.org/packages/Xamarin.Android.MP4Transcoder/
await Xamarin.MP4Transcoder.Transcoder
.For720pFormat()
.ConvertAsync(inputFile, ouputFile, f => {
onProgress?.Invoke((int)(f * (double)100), 100);
});
return ouputFile;
For Previous Android versions
Soruce Code https://github.com/neurospeech/xamarin-android-ffmpeg
Install-Package Xamarin.Android.FFmpeg
Use this as template, this lets you log output as well as calculates progress.
You can take a look at source, this one downloads ffmpeg and verifies sha1 hash on first use.
public class VideoConverter
{
public VideoConverter()
{
}
public File ConvertFile(Context contex,
File inputFile,
Action<string> logger = null,
Action<int,int> onProgress = null)
{
File ouputFile = new File(inputFile.CanonicalPath + ".mpg");
ouputFile.DeleteOnExit();
List<string> cmd = new List<string>();
cmd.Add("-y");
cmd.Add("-i");
cmd.Add(inputFile.CanonicalPath);
MediaMetadataRetriever m = new MediaMetadataRetriever();
m.SetDataSource(inputFile.CanonicalPath);
string rotate = m.ExtractMetadata(Android.Media.MetadataKey.VideoRotation);
int r = 0;
if (!string.IsNullOrWhiteSpace(rotate)) {
r = int.Parse(rotate);
}
cmd.Add("-b:v");
cmd.Add("1M");
cmd.Add("-b:a");
cmd.Add("128k");
switch (r)
{
case 270:
cmd.Add("-vf scale=-1:480,transpose=cclock");
break;
case 180:
cmd.Add("-vf scale=-1:480,transpose=cclock,transpose=cclock");
break;
case 90:
cmd.Add("-vf scale=480:-1,transpose=clock");
break;
case 0:
cmd.Add("-vf scale=-1:480");
break;
default:
break;
}
cmd.Add("-f");
cmd.Add("mpeg");
cmd.Add(ouputFile.CanonicalPath);
string cmdParams = string.Join(" ", cmd);
int total = 0;
int current = 0;
await FFMpeg.Xamarin.FFMpegLibrary.Run(
context,
cmdParams
, (s) => {
logger?.Invoke(s);
int n = Extract(s, "Duration:", ",");
if (n != -1) {
total = n;
}
n = Extract(s, "time=", " bitrate=");
if (n != -1) {
current = n;
onProgress?.Invoke(current, total);
}
});
return ouputFile;
}
int Extract(String text, String start, String end)
{
int i = text.IndexOf(start);
if (i != -1)
{
text = text.Substring(i + start.Length);
i = text.IndexOf(end);
if (i != -1)
{
text = text.Substring(0, i);
return parseTime(text);
}
}
return -1;
}
public static int parseTime(String time)
{
time = time.Trim();
String[] tokens = time.Split(':');
int hours = int.Parse(tokens[0]);
int minutes = int.Parse(tokens[1]);
float seconds = float.Parse(tokens[2]);
int s = (int)seconds * 100;
return hours * 360000 + minutes * 60100 + s;
}
}
I created an app using Unity3D. After developing it for some weeks I tried to generate an apk and test it on my SGII.
Unity returns no errors or warnings when testing the app locally, but when it runs on my phone, it doesn't work.
There are 2 buttons (1 to clock in and 1 to clock out from work). Button 1 takes the date when you clock in, and switches a boolean to allow you to clock out. When running in my phone, button 1 works fine but button 2 doesn't. The button works actually, since it returns every debug.log, but nothing else.
My code looks like this:
function OnGUI()
{
GUI.skin = skin;
if(Functions.firstTime == 0)
{
Functions.setupWiz();
}
switch(currentMenu)
{
case 1:
GUI.BeginGroup(Rect(0, 0, width, height));
if(Functions.birthday == true)
{
debugLog11 = "\n"+happyBirthdayMsg+", "+Functions.userName+"!";
Functions.birthday = false;
}
if(GUI.Button(Rect(buttonTopHMargin,height*.1f + buttonTopVMargin, width*.24f, height*.1f), "ENTRADA", "Box"))
{
if(!clockedIn && clockedOut)
{
var clockIn = Functions.clockIn();
debugLog11 = "\nHora de entrada";
debugLog12 = "\n"+clockIn[3]+":"+clockIn[4]+":"+clockIn[5];
clockedIn = true;
clockedOut = false;
}
else
{
debugLog11 = "\n"+errorMsg1;
debugLog12 = "\n";
}
}
if(GUI.Button(Rect(width - buttonTopHMargin - width*.24f, height*.1f + buttonTopVMargin, width*.24f, height*.1f), "SALIDA", "Box"))
{
if(!clockedOut && clockedIn)
{
// debugLog11 = clockedIn+"\n"+clockedOut;
Functions.clockOut();
var clockOut = Functions.clockOut();
var workedSecondsToday : Array = Functions.calculateDay();
var workedTimeToday = Functions.convertSeconds(parseInt(workedSecondsToday[0].ToString()));
var extraTimeToday = Functions.convertSeconds(parseInt(workedSecondsToday[1].ToString()));
var festTimeToday = Functions.convertSeconds(parseInt(workedSecondsToday[2].ToString()));
if(parseInt(workedSecondsToday[0].ToString()) > 0 && parseInt(workedSecondsToday[1].ToString()) < 1 && parseInt(workedSecondsToday[2].ToString()) < 1)
{
debugLog11 = "\nHora de Salida\nNormal:"; //NORMAL
debugLog12 = "\n"+clockOut[3]+":"+clockOut[4]+":"+clockOut[5]+"\n"+workedTimeToday[1]+":"+workedTimeToday[2]+workedTimeToday[3];
}
else if(parseInt(workedSecondsToday[0].ToString()) > 0 && parseInt(workedSecondsToday[1].ToString()) > 0 && parseInt(workedSecondsToday[2].ToString()) < 1)
{
debugLog11 = "\nHora de Salida\nNormal:\nExtra:"; //NORMAL + EXTRA
debugLog12 = "\n"+clockOut[3]+":"+clockOut[4]+":"+clockOut[5]+"\n"+workedTimeToday[1]+":"+workedTimeToday[2]+workedTimeToday[3]+"\n"+extraTimeToday[0]+"-"+extraTimeToday[1]+":"+extraTimeToday[2]+":"+extraTimeToday[3];;
}
else if(parseInt(workedSecondsToday[0].ToString()) > 0 && parseInt(workedSecondsToday[1].ToString()) < 1 && parseInt(workedSecondsToday[2].ToString()) > 0)
{
debugLog11 = "\nHora de Salida\nNormal:\nFestivo:"; //NORMAL + FESTIVO
debugLog12 = "\n"+clockOut[3]+":"+clockOut[4]+":"+clockOut[5]+"\n"+workedTimeToday[1]+":"+workedTimeToday[2]+workedTimeToday[3]+"\n"+festTimeToday[0]+"-"+festTimeToday[1]+":"+festTimeToday[2]+":"+festTimeToday[3];
}
else if(parseInt(workedSecondsToday[0].ToString()) > 0 && parseInt(workedSecondsToday[1].ToString()) > 0 && parseInt(workedSecondsToday[2].ToString()) > 0)
{
debugLog11 = "\nHora de Salida\nNormal:\nExtra:\nFestivo:"; //NORMAL + EXTRA + FESTIVO
debugLog12 = "\n"+clockOut[3]+":"+clockOut[4]+":"+clockOut[5]+"\n"+workedTimeToday[1]+":"+workedTimeToday[2]+workedTimeToday[3]+"\n"+extraTimeToday[0]+"-"+extraTimeToday[1]+":"+extraTimeToday[2]+":"+extraTimeToday[3]+"\n"+festTimeToday[0]+"-"+festTimeToday[1]+":"+festTimeToday[2]+":"+festTimeToday[3];
}
clockedOut = true;
clockedIn = false;
}
else
{
debugLog01 = mainMsg;
debugLog11 = "\n"+errorMsg2;
debugLog12 = "\n";
}
}
Can't explain myself better since I have no clue about what is happening. Any help will be much appreciated.
Yesterday, i started placing tons of labels everywhere in my code and finally discovered where it stopped.
It was just replacing this...
var sw = new StreamWriter(Application.persistentDataPath+"\\Settings.txt", false);
by...
var sw = new StreamWriter(Application.persistentDataPath+"/Settings.txt", false);
Only windows supports this bar "\" when used for setting paths, but i was thinking all the time that the error was somewhere in the OnGUI function.
Thanks lot to everyone who came helping me :)
On the Unity3D you can find this information:
Work with specific constraints (constraint):
Samsung Galaxy S (30 MB limit to downloadable file size, also problems with UI)
Samsung Galaxy Tab (30 MB limit to downloadable file size)
HTC Desire (40 MB limit to downloadable file size)
Samsung Galaxy S2 (Minor but possibly annoying issue: phone may work in 16-bit mode with Unity and show color banding Edit: a workaround was added to address the GS2 graphics driver, I don't know if it was related to this problem or not)
HTC EVO3D (Must build with Unity 3.4)
HTC Sensation (Must build with Unity 3.4)
Samsung Galaxy S2 (Minor but possibly annoying issue: phone may work in 16-bit mode with Unity and show color banding Edit: a workaround was added to address the GS2 graphics driver, I don't know if it was related to this problem or not)
Unity3D Android Limitations
I am investigating about traffic measuring in android. I am developing on a Galaxy S4 and I programmed a service that catches TrafficStats API one time per minute, saves in SharedPreference the accumulated traffic (AKA BaseTraffic) and saves in database the difference between the current traffic minus BaseTraffic.
The problem is that in short periods (15 min) TrafficStats return an oversized value (1.6 GB per minute) and ever the same value. Someone know if this is a bug or other issue.
My code is the next for get the traffic:
public class TrafficTracker {
public static long getCurrentTraffic() {
long traff = 0;
traff = (TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes());
if (traff > 0) {
return traff;
} else {
throw new UnsupportedOperationException("TrafficStats not supported");
}
}
public static long getTrafficWithOutBase(long baseTraffic) {
return TrafficStats.getTotalTxBytes() + TrafficStats.getTotalRxBytes() - baseTraffic;
}
}
And call this code here:
if (preferences.getBaseTraffic() != null) {
if (TrafficTracker.getCurrentTraffic() > preferences.getBaseTraffic().getByteTraffic()) {
TrafficObject trafficObject = new TrafficObject(new Date(calendar.getTimeInMillis()), TrafficTracker.getTrafficWithOutBase(preferences.getBaseTraffic().getByteTraffic()));
daoTraffic.create(trafficObject);
preferences.setBaseTraffic(new TrafficObject(new Date(System.currentTimeMillis()), preferences.getBaseTraffic().getByteTraffic() + trafficObject.getByteTraffic()));
} else {//when stats are reseted
TrafficObject trafficObject = new TrafficObject(new Date(calendar.getTimeInMillis()), TrafficTracker.getCurrentTraffic());
daoTraffic.create(trafficObject);
preferences.setBaseTraffic(trafficObject);
}
}
** UPDATE **
I found my error :). I replace >= instead of >. Now works properly when it is disconnected from data or wifi.
if (TrafficTracker.getCurrentTraffic() >= preferences.getBaseTraffic().getByteTraffic())
I found my error :). I replace >= instead of >. Now works properly when it is disconnected from data or wifi.
if (TrafficTracker.getCurrentTraffic() >= preferences.getBaseTraffic().getByteTraffic())
I've been asking questions regarding my Android project that continually plots Bluetooth data in real-time.
Basically what I've already done is create a first version of my app by cobbling together some open source code Blueterm and OrientationSensorExample
It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.
Here's a description of the Blueterm open source code I started with (see link above). Blueterm is basically a terminal emulator program that communicates over Bluetooth. It consists of several activities with Blueterm being the most important. It discovers, pairs, and connects with a remote Bluetooth device that supports SPP/RfComm. When connected I can use Blueterm to configure the remote device by sending it commands to turn on sampling, change the number of channels to sample (to one channel), change to format of the incoming data (I like comma separated data), etc
Here's a description of the OrientationSensorExample open source code I started with (see link above). It's basically an example application of the AnroidPlot library. The OrientationSensor activity implements SensorEventListener. This includes overriding onSenorChanged() which is called whenever new orientation sensor data is taken, and it redraws the graph.
Having cobbled together these two open source projects (Blueterm and OrientationSensorExample) into one application (Blueterm) here's a description of how the overall application (Blueterm) works. When I start Blueterm the whole screen emulates a nice blue terminal. From the Options Menu I discover, pair with, connect to, and configure a remote bluetooth device as described above. Once I have configured the remote device, I go again to the Options Menu and select "Plot data" which launches the Plot activity. The terminal emulator goes away, and a nice scrolling real-time plot from the Plot activity shows up.
As far as I can tell there is a background thread that calls an update() method as follows:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
In the update() method I found it convenient to call my Plot.plotData() method and pass it the same date that is passed to the append() method to plot the data. NOTE: This only works if plotData() is a static method. No one has been able to explain why.
Anyway plotData() is a static method and here's how it and it's helper methods look now:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
If it is strongly recommended that plotData() not be a static method and that I should do something else please explain here and how. Thanks!
This might be a question much better suited for Code Review, rather than here. Perhaps you can reformulate to post it there, or trim it a lot to repost it here.
Furthermore, to answer: "It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.", the best advise would be to link you to a book about android, such as: http://commonsware.com/Android/ . Chapters 35 and 36 deal with services, while chapter 20 is about threads. You will never get an answer as complete as those chapters here.