I am a New B to Android. I have been Able to get the Battery Status/Level with the Following Code:
private void BattStatus() {
BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
context.unregisterReceiver(this);
int rawlevel = intent.getIntExtra("level", -1);
int scale = intent.getIntExtra("scale", -1);
int level = -1;
if (rawlevel >= 0 && scale > 0) {
level = (rawlevel * 100) / scale;
}
batteryLevel = level;
BattStatus.setText("Battery Level : " + batteryLevel + "%");
}
};
IntentFilter batteryLevelFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(batteryLevelReceiver, batteryLevelFilter);
}
I would Like to Store the Battery Level In A text file (Using a thread). Code :
public final Runnable DBThread = new Runnable() {
String AllInfo = batteryLevel+"%"+" , "+new SimpleDateFormat("HH:mm , dd.MM.yy ").format(new Date());
public void run() {
try {
Log.d("DBThread","Battery :"+batteryLevel);
Log.d("DBThread","Updating DB");
myDbHelper.CreateAndWriteFile(sdDir+"/", AllInfo );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 2000);
Log.d("DBThread","Updated DB");
Log.d("DBThread",AllInfo);
}
Unfortunately the Battery Status/Level returns 0% in the text file, when I test it using the Log function in the thread it returns the correct value.
Please could some one be so kind to tell me what I am doing wrong or what I am not doing, and maybe provide me with a code snippet as I am new to Development.And Sorry If My Post is incorrect First timer on Stack Overflow :)
Thank you very much!
This is not a Thread. This is a Runnable, which is a piece of code that is made to run in a Thread.
In your case, it runs in the handler thread, most likely the UI Thread.
You probably start your thread before receiving the battery status, hence writing the default value (0) to the file.
You don't need a thread for that. You can write to the file immediately after your receive the broadcast, in the onReceive method.
Edit
There are a few things that don't work in your code. You unregister the broadcast receiver, hence you don't receive the battery level after the first time.
You write the batteryLevel value without knowing if it has indeed been modified
You write every 2 seconds without knowing there has been a change.
I would suggest that you don't unregister the BR, so you receive all battery level change. Then, in the onReceive, you append to the file the date and new value.
Related
I am trying to log the exception plus the user navigation for x amount of time. For example, when my application got an exception I will append it in a text file. Now from that point of time of time, I need to log only for a certain time. e.g., 1 hour. Is it possible to do it? This is the code I wrote to get the exception information and log it in a file.
Please someone help me with this. Thanks in advance.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(handleAppCrash);
}
private Thread.UncaughtExceptionHandler handleAppCrash =
new Thread.UncaughtExceptionHandler() {
#SuppressLint("LongLogTag")
#Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!file.exists()) {
file.mkdir();
}
try {
data = android_version + "#" + Device + "#" + username + "#" + version + "#" + dates + "#" + Logtrace;
File gpxfile = new File(file, fname);
FileWriter writer = new FileWriter(gpxfile,true);
writer.append(data);
writer.flush();
writer.close();
} catch (
Exception e) {
e.printStackTrace();
}
}
};
You are looking for a way to
restart the app
write more logs to the same file that you created in the UncaughtExceptionHandler.
Firstly, to restart the app, you can take the following steps:
Created a pending intent, e.g., in your onCreate (where Intent intent has class scope, already defined, i.e., not just defined within onCreate):
intent = PendingIntent.getActivity(
YourApplication.getInstance().getBaseContext(),
0,
new Intent(getIntent()),
getIntent().getFlags());
After your try/catch in your UncaughtExceptionHandler, start an alarm to trigger your app in some amount of time, e.g., 1 second; and you must follow this with a System.exit();. This is so the current dying app will properly quit, so that in 1 second, when the alarm triggers, it will start the app again (but it won't if the app is still running).
AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, intent);
System.exit(2);
You could optionally store the name of the file (fname in your code) to SharedPreferences (for writing to the same file when the app restarts). Or it could be a fixed hardcoded name that your app knows, and doesn't need to save the name.
You could saved a Boolean in SharedPreferences to let your app know that you are restarting from an uncaught exception.
Secondly, whenever the app starts:
Check the Boolean in SharedPreferences; if it is a normal start, proceed like normal.
If it is a restart after uncaught exception, then retrieve the file name from SharedPreferences (or get it hard coded), then you can write more logs to the file. As in Android, a file is uniquely determined by path and file name. With the same file name, you can open the same file.
Define a background service
To do anything in the background for a prolonged time you should use a service. I'm using a JobService in this example.
<service android:name="org.example.LogService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":logprocess"/>
Note the process tag, it is important, because you'll want to kill your current process after an uncaught exception.
public class LogService extends JobService {
private Thread thread;
#Override
public boolean onStartJob(final JobParameters params) {
if (!file.exists()) {
file.mkdir();
}
thread = new Thread(() -> {
try {
data = params.getExtras().getString("data");
File gpxfile = new File(file, fname);
FileWriter writer = new FileWriter(gpxfile,true);
writer.append(data);
//continue to write your logs to the file here for as long as you want. you could copy logcat to the file for example.
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
jobFinished(params, false);
});
thread.start();
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
thread.interrupt();
return true;
}
}
Call your background service
public void uncaughtException(Thread thread, Throwable ex) {
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
PersistableBundle extras = new PersistableBundle();
extras.putString("data", android_version + "#" + Device + "#" + username + "#" + version + "#" + dates + "#" + Logtrace);
JobInfo.Builder builder = new JobInfo.Builder(0, new ComponentName(context, LogService.class))
builder.setExtras(extras);
builder.setOverrideDeadline(1000);
scheduler.schedule(builder.build());
//stop the current process.
Process.killProcess(Process.myPid());
System.exit(10);
}
Killing the current process is optional but recommended, the application could be in a bad state after an uncaught exception. If you want to restart your application you can call startActivity from the LogService.
Note:
You've not given any details on what exactly you want to log for x amount of time, so this code only has a comment where the log collection goes.
Note 2:
This code was adapted from the ACRA project of which I am a maintainer, specifically manifest JobSenderService DefaultSenderScheduler and ProcessFinisher
As I understand it, setting an AccessibilityService's notificationTimeout property should limit onAccessibilityEvent from being called more frequently than that timeout. I have tried setting this both in the accessibility service's xml file and programmatically with setServiceInfo.
However no matter what I set it to, I get very frequent calls of onAccessibilityEvent.
Here's some of my code:
XML:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackAllMask"
android:accessibilityFlags="flagReportViewIds|flagIncludeNotImportantViews"
android:canRetrieveWindowContent="true"
android:description="#string/accessibility_service_description"
android:notificationTimeout="100">
JAVA:
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
accessibilityServiceInfo.notificationTimeout = 1000;
setServiceInfo(accessibilityServiceInfo);
Utils.logDebug(TAG, "TIMEOUT: " + getServiceInfo().notificationTimeout);
long currentTime = System.currentTimeMillis();
long timeSinceLastEvent = currentTime - timeLastAccessibilityEvent;
Utils.logDebug(TAG, "onAccessibilityEvent(), type: " + event.getEventType() + ", last event: " + timeSinceLastEvent + "ms ago");
if(!event.equals(lastAccessibilityEvent) && timeSinceLastEvent < MAX_FREQUENCY_ACCESSIBILITY_EVENT_MS) {
Utils.logDebug(TAG, "Too soon, returning!");
timeLastAccessibilityEvent = currentTime;
return;
}
timeLastAccessibilityEvent = currentTime;
}
The notificationTimeout debug log reads correctly for whatever I set it to, but I get calls as frequent as 0ms old even though it should be waiting a full second!
Here is a similar question: Notification Timeout - Specifying delay between accessibility events
By reading the source code, you can find that if the event type is WINDOW_CONTNET_CHANGE, the notification timeout is useless. It seems it is by design. Maybe they want WINDOW_CONTENT_CHANGE event notice as soon as possible.
You can change the event type in your xml by other TYPEs inside of WINDOW_CONTNET_CHANGE, then the notification timeout will work.
If you have to use WINDOW_CONTNET_CHANGE, you can set a timer to drop extra incoming events in your onAccessibilityEvent().
https://github.com/aosp-mirror/platform_frameworks_base/blob/master/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java#L1219
if ((mNotificationTimeout > 0)
**&& (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)**) {
// Allow at most one pending event
final AccessibilityEvent oldEvent = mPendingEvents.get(eventType);
mPendingEvents.put(eventType, newEvent);
if (oldEvent != null) {
mEventDispatchHandler.removeMessages(eventType);
oldEvent.recycle();
}
message = mEventDispatchHandler.obtainMessage(eventType);
} else {
// Send all messages, bypassing mPendingEvents
message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
}
message.arg1 = serviceWantsEvent ? 1 : 0;
mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout)
I am building a simple Android app to check if active internet connection is available on my phone. For checking this I am using a service which is running into background every 30 seconds and "pings" the Google's DNS server to check if there is a response.
This is my class used for checking internet status:
public class NetworkStatus {
private static final String GOOGLE_DNS_SERVER = "8.8.8.8";
private static final String CLOUDFLARE_DNS_SERVER = "1.1.1.1";
private static final String TAG = "OUTGOING-NET-STATUS";
private static final String RETRY_TAG = "DNS-CHECK";
boolean pingDnsServerSuccessful() {
boolean success = false;
int count = 0;
final int MAX_TRIES = 15;
while (!success && count++ < MAX_TRIES) {
Log.d(RETRY_TAG, "Retry value: " + count + " out of " + MAX_TRIES);
success = isDnsServerReachable(CLOUDFLARE_DNS_SERVER) || isDnsServerReachable(GOOGLE_DNS_SERVER);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (success) {
Log.d(TAG, "Outgoing Internet Traffic is Enabled");
} else {
Log.d(TAG, "Error reaching Outgoing Traffic");
}
return success;
}
private boolean isDnsServerReachable(String dnsServer) {
int connectionTimeout = 250;
try {
return InetAddress.getByName(dnsServer).isReachable(connectionTimeout);
} catch (Exception e) {
Log.d(TAG, "Exception: Error while pinging " + dnsServer + " DNS server: " + e);
}
return false;
}
}
and this is the code which triggers the background timer within the service:
public void startTimer() {
//set a new Timer
timer = new Timer();
networkStatus = new NetworkStatus();
notificationManager = new NotificationManager();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 30 seconds
timer.schedule(timerTask, 1000, 30000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ " + (counter++));
if (networkStatus.pingDnsServerSuccessful()) {
Log.i(PING_TAG, "Active Internet Connection");
if (!notificationManager.getLastSentNotificationType().equals(NotificationType.INTERNET_UP)) {
Log.i(NOTIFICATION_TAG, "Internet ON");
notificationManager.sendNotification(getApplicationContext(), NotificationType.INTERNET_UP);
}
} else {
Log.i(PING_TAG, "No Internet Connection");
if (!notificationManager.getLastSentNotificationType().equals(NotificationType.INTERNET_DOWN)) {
Log.i(NOTIFICATION_TAG, "Internet OFF");
notificationManager.sendNotification(getApplicationContext(), NotificationType.INTERNET_DOWN);
}
}
}
};
}
Everything works fine for a random amount of time (minutes/hours) when suddenly the DNS server can't be reached anymore when app is running in background and phone is locked. Activating the phone's screen is immediately resulting into a success while pinging the server.
Here are some logs reflecting the behaviour:
Logs
Does anybody have a clue why is this happening? I highly doubt that Google or Cloudflare DNS server becomes unresponsive...
Many Thanks!
This is Doze mode. To save power, background requests are limited to a small window every 15 minutes or so for requests. In addition, background processes can be killed at any time.
I'm not sure what you're actually trying to do, but there are 100% better ways to do it. For example, JobScheduler allows you to schedule a job to go off only if the internet is connected. No need to ping a server manually, and no need to do all the work to avoid Doze.
I have a code that reads my inbox and stores each read SMS as a separate file in a fixed folder which i need to upload on FTP later on. I am using the intent for FTP Upload. My program stucture is something like: onCreate() -> Method1 inbox read -> Delete inbox messages -> Method2 Upload FTP -> Method3 Delete Uploaded Folder -> Further Tasks The problem is that Further Tasks are called before the app is done uploading the folder contents and the server is simply disconnected. I tried calling Method3 in the Further Tasks with the help of a Handler set at delay of 10 minutes but it didn't help as upload may take a lot more time than that also it may not have any files at all to upload so those 10 minutes are wasted. I want the app to wait till the upload is complete. So the question is: What is the proper way of doing this?
EDIT :
The code i am using:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ReadnDeleteSMS();
}
public void FTPUpload(){ //FTPUpload via ANDFTP app
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
Uri ftpUri = Uri.parse("Server"); //Server Call
intent.setDataAndType(ftpUri, "vnd.android.cursor.dir/lysesoft.andftp.uri");
intent.putExtra("command_type", "upload");
intent.putExtra("ftp_username", "username");
intent.putExtra("ftp_password", "password");
intent.putExtra("ftp_pasv", "true");
intent.putExtra("ftp_resume", "true");
intent.putExtra("ftp_encoding", "UTF-8");
intent.putExtra("progress_title", "Uploading folder ...");
intent.putExtra("local_file1", "/sdcard/ReceivedSMS");
intent.putExtra("remote_folder", "remote folder");
intent.putExtra("close_ui", "true"); //Finally start the Activity
startActivityForResult(intent, RESULT_OK); i++;
customHandler.postDelayed(finalizer, 10*60*1000);}
public void ReadnDeleteSMS(){ //Reads, BackUps and Deletes Inbox Messages
Cursor cursor1 = getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);
if (cursor1.getCount() > 0) {
while (cursor1.moveToNext()){
int id = cursor1.getInt(0);
String address = cursor1.getString(cursor1.getColumnIndex("address"));
String date = cursor1.getString(cursor1.getColumnIndex("date"));
String SMSDate = DateConversion(date);
String msg = cursor1.getString(cursor1.getColumnIndex("body"));
ReadSMS = address + "\n" + SMSDate + "\n" + msg + "\n";
FileName(zText);
myDate = zText.toString(); zText = new StringBuilder();
fileWrite("/sdcard/ReceivedSMS/" + myDate, ReadSMS);
try{
getApplicationContext().getContentResolver().delete(Uri.parse("content://sms/" + id), null, null);
Toast.makeText(getBaseContext(), "Successfully Deleted", Toast.LENGTH_SHORT).show();}
catch(Exception e){
Toast.makeText(getBaseContext(), "Error Deleting", Toast.LENGTH_SHORT).show();}}}
FTPUpload();}
public String DateConversion (String date){ //Proper Date Format Display
Long timestamp = Long.parseLong(date);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
return calendar.getTime().toString();}
public void FileName(StringBuilder zText) { //Inbox Message File Name
SimpleDateFormat mSDF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
myDate = mSDF.format(new Date());
myDate = myDate.replace(" ", "");
myDate = myDate.replace("/", "");
myDate = myDate.replace(":", "");
myDate = myDate.replace(".", "");
zText.append(myDate);}
public Runnable finalizer = new Runnable(){ //Main Handler
public void run(){
if (i > 0)
{DeleteDirectory("/sdcard/ReceivedSMS"); i = 0;}
//Some Further Tasks
}
These further tasks are to be called often but if the upload is under execution, no such tasks must perform. The tasks include reading a webpage, String editing and such. These are the main tasks of my app.
Without posting some of the code you are using to do this process is difficult to help you but if your problem is that you have to wait until one task finishes to start another then that seems a job for sendBroadcast and BroadcastReceiver.
Example
Say you handle the logic of the ftp upload in a Service (for simplicity, this logic could be on an AsyncTask or anything similar):
public class RefreshService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
// Backgroud process here
// ...
// When the task is completed
sendBroadcast(new Intent("com.example.action.REFRESH_COMPLETED"));
}
}
}
In order to catch that com.example.action.REFRESH_COMPLETED you have to provide a BroadcastReceiver.
public class RefreshCompletedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// The logic you want to execute right after the background task is completed
}
}
You also have to state that this class should catch the specific action you defined before. To do this, update your manifest and include the receiver:
...
<service
android:name=".RefreshService"
android:exported="false" >
</service>
<receiver android:name=".RefreshCompletedReceiver" >
<intent-filter>
<action android:name="com.example.action.REFRESH_COMPLETED" />
</intent-filter>
</receiver>
...
And that's it. The code in the BroadcastReceiver will execute after the background process finishes. Keep in mind that you'll need to handle an interruption of the process or similar network errors and only call sendBroadcast when the operation was successful.
Additional Info
You can dynamically register the receiver using registerReceiver in the context but you'll have to unregister it (typically on the onDestroy method of your activity) or your app will crash (see the reference).
This helped me a lot with this sort of problems.
Also check this question to choose between Service and AsyncTask.
Check the docs here and here for more info on both.
Disclaimer
I just started programming in android so there might be another way to do this, I just found this way easy to understand and easier to implement.
First of all sorry to repost the question as I didn't post my code earlier just written some lines now I am failing to explain.So posting the question again with codes.
I have written a code that will log the acceleromter data and save the data in a csv file. What I need to do is I have to run the acceleromter in background as a service. So,I m done with writing the codes for
1.running the accelerometer,
2.shown the data in textbox,
3.write the data in a csv file.
Things that are left is making it a service. I m giving the code below(Howfar I have done)
#SuppressWarnings("deprecation")
public class AccelerometerVaue extends Activity implements SensorListener {
SensorManager sm = null;
TextView xacc= null;
TextView yacc = null;
TextView zacc = null;
TextView xorient = null;
TextView yorient = null;
TextView zorient = null;
TextView text = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sm = (SensorManager) getSystemService(SENSOR_SERVICE);
setContentView(R.layout.activity_main);
xacc = (TextView) findViewById(R.id.xvalue);
yacc = (TextView) findViewById(R.id.yvalue);
zacc = (TextView) findViewById(R.id.zvalue);
xorient = (TextView) findViewById(R.id.xvalues);
yorient = (TextView) findViewById(R.id.yvalues);
zorient = (TextView) findViewById(R.id.zvalues);
}
#SuppressLint("SdCardPath")
public void onSensorChanged(int sensor, float[] values) {
synchronized (this) {
if (sensor == SensorManager.SENSOR_ORIENTATION) {
xorient.setText("Orientation X: " + values[0]);
yorient.setText("Orientation Y: " + values[1]);
zorient.setText("Orientation Z: " + values[2]);
}
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
// Time now = new Time();
// now.setToNow();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String currentDateandTime = sdf.format(new Date());
Log.d("Time",currentDateandTime);
xacc.setText("Accel X: " + values[0]);
yacc.setText("Accel Y: " + values[1]);
zacc.setText("Accel Z: " + values[2]);
String res=String.valueOf(currentDateandTime+"#"+values[0])+"#"+String.valueOf(values[1])+"#"+String.valueOf(values[2]);
Log.d("test", res);
CSVWriter writer = null;
try
{
//Log.d("check","pasla");
//Environment.getExternalStorageDirectory().getPath();
writer = new CSVWriter(new FileWriter("/sdcard/AccelData.csv",true), ',');
String[] entries = res.split("#"); // array of your values
writer.writeNext(entries);
//FileWriter
writer.close();
}
catch (IOException e)
{
//error
}
}
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
String tag = null;
Log.e(tag,"onAccuracyChanged: " + sensor + ", accuracy: " + accuracy);
}
#Override
protected void onResume() {
super.onResume();
sm.registerListener(this,
SensorManager.SENSOR_ORIENTATION |
SensorManager.SENSOR_ACCELEROMETER,
SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onStop() {
sm.unregisterListener(this);
super.onStop();
}
}
So, now I need to run a background service which will keep taking accelerometer data in background and also keep saving into that csv file. How can I do this? One more I like to ask is it possible to take the acc data after a certain period of time? Suppose here I m logging all the data continuously which is huge and thew battery drain will be huge. So, I need to implemented the service in such a way so that it will run in background for 15mins and log the data for 10 seconds at the beginning of each minute.
I have just replied to a similar question.
You can use Android's scheduling mechanism, called AlarmManager, to schedule your services.
There is a problem inerent to this approach, though: when the system is in battery-saving sleep state, your service has to acquire a wake lock in order to execute properly, then when finished release the wake lock for the system to go back to sleep state. Implementing this wake lock acquisition/release mechanism is not a trivial task.
I recommend using Commonsware's WakefulIntentService in order to avoid writing your own wake lock acquisition/release mechanism for your service. It's very easy to use. You will basically declare a class that extends the WakefulIntentService class and override its doWakefulWork() method. In that method you will poll for accelerometer data and write it to a file. I'll just let you down in the part of displaying the data in the screen, I don't know how to do that (I suppose you will have to perform a bind to the service though). <:)