My program saves some data to SQLITE with datetime. There is a button save, which saves the value of 1 spinner along with datetime.
I will provide an example to make sure all understand:
User inputs at 20:03:24, now i want to disable the save button until the HOUR is at least 21:00. So every save can only be on every full hour.
Now my idea is to read the hour from SQLITE and check if the last inputed hour is != current Hour then
btn.setClickable(true);
else
btn.setClickable(false);
This is what i have created and partially works.
public void CHECK(){
sqliteDbHelper_ = new Sqlite_DBHelper(getActivity());
sqLiteDatabase = sqliteDbHelper_.getWritableDatabase();
String[] columns = {"hour_only","JAKOST"};
cursor = sqLiteDatabase.query("podatki", columns,null,null,null,null,null);
Calendar calander = Calendar.getInstance();
Integer currentHour = calander.get(Calendar.HOUR_OF_DAY);
gumb_poslji.setClickable(true);
cursor.moveToLast();
Integer lastInputedHour = cursor.getInt(0);
Log.e(TAG+"zadnja vnesena URA",""+lastInputedHour);
Log.e(TAG+"trenutna URA",""+currentHour);
if (lastInputedHour == currentHour){
gumb_poslji.setBackgroundColor(getResources().getColor(R.color.TextMainColor));
gumb_poslji.setClickable(false);
}
else
gumb_poslji.setClickable(true);
gumb_poslji.setBackgroundColor(getResources().getColor(R.color.colorGreen));
}
How to implement a listener on the app that will constantly looking if there is a difference in the app?
Also be aware the button must also change state from clicable to not clicable even if a user is in the app and the hour changes.
Since i am new to java and Android i am looking for an idea or an example how to do this, not a solution. Am i thinking in a right direction?
I use below code to clear missed calls after I launch my app. In this I get rows affeted is 1. But when i get next missed call, at that time android's stock phone app adds a new notification as "2 new missed calls". Means they are not counting my clear. Am I missing something.
Note: If i launch the stock phone app once, the counter is reset to 0 again.
public boolean markMissedCallsAsRead() {
ContentValues values = new ContentValues();
values.put(CallLog.Calls.NEW, Integer.valueOf(0));
values.put(CallLog.Calls.IS_READ, Integer.valueOf(1));
StringBuilder where = new StringBuilder();
where.append(CallLog.Calls.NEW);
where.append(" = 1 AND ");
//where.append(CallLog.Calls.IS_READ).append(" = 0");
//where.append(" AND ");
where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
int rows = context.getContentResolver().update(CallLog.Calls.CONTENT_URI, values, where.toString(),
null);
Utilities.writeToLogFile(Constants.LOG_ERROR_LEVEL, "cleared call logs " + rows);
return true;
}
I'm facing the same problem. There is a similar question here:
https://stackoverflow.com/a/26564121/6433463
I'm starting to think (as stated in that thread) that the only way to achieve that is by opening the stock call log (I hope I'm wrong, but couldn't find anything else).
I'm trying to use UsageStatsManager to get the foreground app on a Nexus 5 with Marshmallow. I remember it used to work, but for some reason I'm getting null strings for package/class names now.
Here's my implementation
public String[] getForegroundPackageNameClassNameByUsageStats() {
String packageNameByUsageStats = null;
String classByUsageStats = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService("usagestats");
final long INTERVAL = 1000;
final long end = System.currentTimeMillis();
final long begin = end - INTERVAL;
final UsageEvents usageEvents = mUsageStatsManager.queryEvents(begin, end);
while (usageEvents.hasNextEvent()) {
UsageEvents.Event event = new UsageEvents.Event();
usageEvents.getNextEvent(event);
if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
packageNameByUsageStats = event.getPackageName();
classByUsageStats = event.getClassName();
Log.d(TAG, "packageNameByUsageStats is" + packageNameByUsageStats + ", classByUsageStats is " + classByUsageStats);
}
}
}
return new String[]{packageNameByUsageStats,classByUsageStats};
}
For some reason, it doesn't go into the while loop, i.e. usageEvents.hasNextEvent() is false. Because of this, it returns null package/class names.
Any idea what I'm doing wrong?
Thanks.
OK, so I found that once I set the interval to 10000 instead of 1000, it works. Apparently a 1s interval is too small.
I am using this myself. I think the usage stats will only be updated when an app comes to foreground. So if the foreground app got to the foreground (and stayed) before your 'begin' timestamp then you will not get it. :(
On the other hand when you use a long time ago you will get a giant list where you only need the highest time to determine the foreground app.
So what I do is I create 3 different times: 1min ago, 1 hour ago and 12 hours ago.
When I get an empty list on 1min I repeat request with 1h and so on. That way I get foreground most of the time. But I never get it to work ALL of the time.
I really miss the old way of just asking the package manager which app is foreground (prior to android 5), the new way is a bit messy.
My app is crashing when I create a new comment for a post with notifyDataSetChanged() that is linked to an adapter that shows time. All Data is taken from parse.com where I store all my database information. The freshly made comment is supposed to immediately shown in the listview in the same activity showing the comment and the time. The time is taken by createdAt() that is substracted by currentTime.
the error says:
java.lang.NullPointerException: Attempt to invoke virtual method 'long java.util.Date.getTime()' on a null object reference
and this is the button that will add the comment:
//Add a new comment
mAddComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("log", "add button clicked");
final ParseObject myComment = new ParseObject("Comments");
final String comment = mComment.getText().toString();
myComment.put("CommentOwner", currentUserUsername);
myComment.put("Comment", comment);
myComment.put("SocialId", ParseObject.createWithoutData("Social", socialId));
myComment.saveInBackground();
adapter.setNotifyOnChange(false);
adapter.add(myComment);
adapter.notifyDataSetChanged();
setListAdapter(adapter);
if (adapter.isEmpty()) {
} else {
mList.setSelection(mList.getAdapter().getCount() - 1);
}
mComment.setText("");
}
});
and this is the thing inside the adapter that shows the time:
public SocialCommentAdapter(Context context, List<ParseObject> Comment) {
super(context, R.layout.socialcommentcustomlayout, Comment);
mContext = context;
mComment = Comment;
}
.
.
.
.
.
final ParseObject commentObject = mComment.get(position);
//get Time
Date created = commentObject.getCreatedAt();
Date currentDate = new Date();
long timepast = currentDate.getTime() - created.getTime(); <<<< The Error points to this line of the code
long days = TimeUnit.MILLISECONDS.toDays(timepast);
timepast -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(timepast);
timepast -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(timepast);
timepast -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(timepast);
StringBuilder sb = new StringBuilder(64);
if( days > 0){
sb.append(days);
sb.append("d");
}else if (hours>0){
sb.append(hours);
sb.append("h");
} else if (minutes >0){
sb.append(minutes);
sb.append("m");
}else if ( seconds >0){
sb.append(seconds);
sb.append("s");
}
holder.timeComment.setText(sb.toString());
Do you guys know what's wrong with this? Can you guys help me out?
Thank you
I know you just answered your own question, but I was in the middle of writing my answer and wanted to add an explanation as to why your variable is null, so I'll just post my answer anyway.
First of all, commentObject.getCreatedAt(); a few lines above your crash is returning null and so your created object is null, and that is the cause of your crash. Now for an explanation.
Reading the documentation on getCreatedAt(), we see that it returns the time which will be
...the creation time will be the time of the first ParseObject.save() call rather than the time the object was created locally.
Now, we can see that you have called myComment.saveInBackground(); in your onClick method on a new ParseObject, and added that object to a list. You then call adapter.notifyDataSetChanged(); which triggers the getView method where your code is crashing. Your saveInBackground method should save the object, but, it is going to do it in the background... a different thread from the thread you are creating the object on.
So the way I understand the documentation and what we are seeing here, getCreatedAt() will return null until the ParseObject is saved, at which time it will return the Date object with the correct data. Because you are calling saveInBackground instead of save, the saving happens on a background thread, which means that the saving probably doesn't happen right away. The execution order is as follows:
You Initialize the ParseObject
You call saveInBackground on the object, saving probably hasn't started yet
You add the object to your adapter and call notify
getView gets triggered and your app crashes because the Date is still null
Maybe around here (in some cases earlier) the save will be complete and Date won't be null anymore if your app doesn't crash.
The way you can fix it is by checking if the variable is null:
if (created != null) {
long timepast = currentDate.getTime() - created.getTime();
.
. // your date forming code here
.
holder.timeComment.setText(sb.toString());
} else {
holder.timeComment.setText("new");
}
Hope this helps explain why you were getting a null object.
I found something that can be a solution for me.
I believe the problem is that created time is null.
so I solved this problem by creating an if/else method as follow:
//get Time
Date created = commentObject.getCreatedAt();
Date currentDate = new Date();
if( created == null ){
holder.timeComment.setText("just now");
}else{
long timepast = currentDate.getTime() - created.getTime();
long days = TimeUnit.MILLISECONDS.toDays(timepast);
timepast -= TimeUnit.DAYS.toMillis(days);
long hours = TimeUnit.MILLISECONDS.toHours(timepast);
timepast -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(timepast);
timepast -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(timepast);
StringBuilder sb = new StringBuilder(64);
if( days > 0){
sb.append(days);
sb.append("d");
}else if (hours>0){
sb.append(hours);
sb.append("h");
} else if (minutes >0){
sb.append(minutes);
sb.append("m");
}else if ( seconds >0){
sb.append(seconds);
sb.append("s");
}
holder.timeComment.setText(sb.toString());
}
This way, if it cannot get the created time, it will just say "just now". this works well for me.
I'm developing an application where I need to insert lots of Contact entries. At the current time approx 600 contacts with a total of 6000 phone numbers. The biggest contact has 1800 phone numbers.
Status as of today is that I have created a custom Account to hold the Contacts, so the user can select to see the contact in the Contacts view.
But the insertion of the contacts is painfully slow. I insert the contacts using ContentResolver.applyBatch. I've tried with different sizes of the ContentProviderOperation list(100, 200, 400), but the total running time is approx. the same. To insert all the contacts and numbers takes about 30 minutes!
Most issues I've found regarding slow insertion in SQlite brings up transactions. But since I use the ContentResolver.applyBatch-method I don't control this, and I would assume that the ContentResolver takes care of transaction management for me.
So, to my question: Am I doing something wrong, or is there anything I can do to speed this up?
Anders
Edit:
#jcwenger:
Oh, I see. Good explanation!
So then I will have to first insert into the raw_contacts table, and then the datatable with the name and numbers. What I'll lose is the back reference to the raw_id which I use in the applyBatch.
So I'll have to get all the id's of the newly inserted raw_contacts rows to use as foreign keys in the data table?
Use ContentResolver.bulkInsert (Uri url, ContentValues[] values) instead of ApplyBatch()
ApplyBatch (1) uses transactions and (2) it locks the ContentProvider once for the whole batch instead locking/unlocking once per operation. because of this, it is slightly faster than doing them one at a time (non-batched).
However, since each Operation in the Batch can have a different URI and so on, there's a huge amount of overhead. "Oh, a new operation! I wonder what table it goes in... Here, I'll insert a single row... Oh, a new operation! I wonder what table it goes in..." ad infinitium. Since most of the work of turning URIs into tables involves lots of string comparisons, it's obviously very slow.
By contrast, bulkInsert applies a whole pile of values to the same table. It goes, "Bulk insert... find the table, okay, insert! insert! insert! insert! insert!" Much faster.
It will, of course, require your ContentResolver to implement bulkInsert efficiently. Most do, unless you wrote it yourself, in which case it will take a bit of coding.
bulkInsert: For those interested, here is the code that I was able to experiment with. Pay attention to how we can avoid some allocations for int/long/floats :) this could save more time.
private int doBulkInsertOptimised(Uri uri, ContentValues values[]) {
long startTime = System.currentTimeMillis();
long endTime = 0;
//TimingInfo timingInfo = new TimingInfo(startTime);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
DatabaseUtils.InsertHelper inserter =
new DatabaseUtils.InsertHelper(db, Tables.GUYS);
// Get the numeric indexes for each of the columns that we're updating
final int guiStrColumn = inserter.getColumnIndex(Guys.STRINGCOLUMNTYPE);
final int guyDoubleColumn = inserter.getColumnIndex(Guys.DOUBLECOLUMNTYPE);
//...
final int guyIntColumn = inserter.getColumnIndex(Guys.INTEGERCOLUMUNTYPE);
db.beginTransaction();
int numInserted = 0;
try {
int len = values.length;
for (int i = 0; i < len; i++) {
inserter.prepareForInsert();
String guyID = (String)(values[i].get(Guys.GUY_ID));
inserter.bind(guiStrColumn, guyID);
// convert to double ourselves to save an allocation.
double d = ((Number)(values[i].get(Guys.DOUBLECOLUMNTYPE))).doubleValue();
inserter.bind(guyDoubleColumn, lat);
// getting the raw Object and converting it int ourselves saves
// an allocation (the alternative is ContentValues.getAsInt, which
// returns a Integer object)
int status = ((Number) values[i].get(Guys.INTEGERCOLUMUNTYPE)).intValue();
inserter.bind(guyIntColumn, status);
inserter.execute();
}
numInserted = len;
db.setTransactionSuccessful();
} finally {
db.endTransaction();
inserter.close();
endTime = System.currentTimeMillis();
if (LOGV) {
long timeTaken = (endTime - startTime);
Log.v(TAG, "Time taken to insert " + values.length + " records was " + timeTaken +
" milliseconds " + " or " + (timeTaken/1000) + "seconds");
}
}
getContext().getContentResolver().notifyChange(uri, null);
return numInserted;
}
An example of on how to override the bulkInsert(), in order to speed up multiples insert, can be found here
#jcwenger At first, after read your post, I think that's the reason of
bulkInsert is quicker than ApplyBatch, but after read the code of Contact Provider, I don't think so.
1.You said ApplyBatch use transactions, yes, but bulkInsert also use transactions. Here is the code of it:
public int bulkInsert(Uri uri, ContentValues[] values) {
int numValues = values.length;
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransactionWithListener(this);
try {
for (int i = 0; i < numValues; i++) {
Uri result = insertInTransaction(uri, values[i]);
if (result != null) {
mNotifyChange = true;
}
mDb.yieldIfContendedSafely();
}
mDb.setTransactionSuccessful();
} finally {
mDb.endTransaction();
}
onEndTransaction();
return numValues;
}
That is to say, bulkInsert also use transations.So I don't think that's the reason.
2.You said bulkInsert applies a whole pile of values to the same table.I'm sorry I can't find related code in the source code of froyo.And I want to know how could you find that?Could you tell me?
The reason I think is that:
bulkInsert use mDb.yieldIfContendedSafely() while applyBatch use
mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)/*SLEEP_AFTER_YIELD_DELAY = 4000*/
after reading the code of SQLiteDatabase.java, I find that, if set a time in yieldIfContendedSafely, it will do a sleep, but if you don't set the time, it will not sleep.You can refer to the code below which is a piece of code of SQLiteDatabase.java
private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
if (mLock.getQueueLength() == 0) {
// Reset the lock acquire time since we know that the thread was willing to yield
// the lock at this time.
mLockAcquiredWallTime = SystemClock.elapsedRealtime();
mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
return false;
}
setTransactionSuccessful();
SQLiteTransactionListener transactionListener = mTransactionListener;
endTransaction();
if (checkFullyYielded) {
if (this.isDbLockedByCurrentThread()) {
throw new IllegalStateException(
"Db locked more than once. yielfIfContended cannot yield");
}
}
if (sleepAfterYieldDelay > 0) {
// Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
// check if anyone is using the database. If the database is not contended,
// retake the lock and return.
long remainingDelay = sleepAfterYieldDelay;
while (remainingDelay > 0) {
try {
Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
} catch (InterruptedException e) {
Thread.interrupted();
}
remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
if (mLock.getQueueLength() == 0) {
break;
}
}
}
beginTransactionWithListener(transactionListener);
return true;
}
I think that's the reason of bulkInsert is quicker than applyBatch.
Any question please contact me.
I get the basic solution for you,
use "yield points" in batch operation.
The flip side of using batched operations is that a large batch may lock up the database for a long time preventing other applications from accessing data and potentially causing ANRs ("Application Not Responding" dialogs.)
To avoid such lockups of the database, make sure to insert "yield points" in the batch. A yield point indicates to the content provider that before executing the next operation it can commit the changes that have already been made, yield to other requests, open another transaction and continue processing operations.
A yield point will not automatically commit the transaction, but only if there is another request waiting on the database. Normally a sync adapter should insert a yield point at the beginning of each raw contact operation sequence in the batch. See withYieldAllowed(boolean).
I hope it's may be useful for you.
Here is am example of inserting same data amount within 30 seconds.
public void testBatchInsertion() throws RemoteException, OperationApplicationException {
final SimpleDateFormat FORMATTER = new SimpleDateFormat("mm:ss.SSS");
long startTime = System.currentTimeMillis();
Log.d("BatchInsertionTest", "Starting batch insertion on: " + new Date(startTime));
final int MAX_OPERATIONS_FOR_INSERTION = 200;
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for(int i = 0; i < 600; i++){
generateSampleProviderOperation(ops);
if(ops.size() >= MAX_OPERATIONS_FOR_INSERTION){
getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops);
ops.clear();
}
}
if(ops.size() > 0)
getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY,ops);
Log.d("BatchInsertionTest", "End of batch insertion, elapsed: " + FORMATTER.format(new Date(System.currentTimeMillis() - startTime)));
}
private void generateSampleProviderOperation(ArrayList<ContentProviderOperation> ops){
int backReference = ops.size();
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
.withValue(ContactsContract.RawContacts.AGGREGATION_MODE, ContactsContract.RawContacts.AGGREGATION_MODE_DISABLED)
.build()
);
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "GIVEN_NAME " + (backReference + 1))
.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, "FAMILY_NAME")
.build()
);
for(int i = 0; i < 10; i++)
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, backReference)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MAIN)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, Integer.toString((backReference + 1) * 10 + i))
.build()
);
}
The log:
02-17 12:48:45.496 2073-2090/com.vayosoft.mlab D/BatchInsertionTest﹕ Starting batch insertion on: Wed Feb 17 12:48:45 GMT+02:00 2016
02-17 12:49:16.446 2073-2090/com.vayosoft.mlab D/BatchInsertionTest﹕ End of batch insertion, elapsed: 00:30.951
Just for the information of the readers of this thread.
I was facing performance issue even if using applyBatch().
In my case there was database triggers written on one of the table.
I deleted the triggers of the table and its boom.
Now my app insert rows with blessing fast speed.