/*
* Copyright 2013 i'm SpA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.imwatch.nfclottery;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import it.imwatch.nfclottery.data.provider.NFCMLContent;
import java.util.ArrayList;
import java.util.List;
import static it.imwatch.nfclottery.Const.DEBUG;
/**
* An helper for the data-handling operations.
*/
public class DataHelper {
private static final String TAG = DataHelper.class.getSimpleName();
public static final String VALUES_SEPARATOR = "ยง";
/**
* Inserts the given contact into the local Content Provider.
*
* @param context The current context
* @param names The names of the contact
* @param emailsList The email address of the contact to insert
* @param organizations The organization of the contact to insert
* @param titles The title of the contact to insert
*/
public static boolean insertContact(Context context, ArrayList<String> names, String emailsList,
ArrayList<String> organizations, ArrayList<String> titles) {
if (DEBUG) Log.v(TAG, "Preparing contact data for insertion");
String namesString, orgStrings, titlesString;
namesString = packList(names);
orgStrings = packList(organizations);
titlesString = packList(titles);
// Remove all heading and trailing separators in the emails field (ensures there's only one at each end)
emailsList = cleanupSeparators(emailsList);
emailsList = VALUES_SEPARATOR + emailsList + VALUES_SEPARATOR; // See isEmailAlreadyPresent for info
if (DEBUG) {
Log.v(TAG, String.format("Inserting contact into DB.\n\t> Name(s): %s\n\t> Email(s): %s\n" +
"\t> Organization(s): %s\n\t> Title(s): %s",
namesString, emailsList, orgStrings, titlesString));
}
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(NFCMLContent.Geeks.Columns.EMAIL.getName(), emailsList);
cv.put(NFCMLContent.Geeks.Columns.NAME.getName(), namesString);
cv.put(NFCMLContent.Geeks.Columns.ORGANIZATION.getName(), orgStrings);
cv.put(NFCMLContent.Geeks.Columns.TITLE.getName(), titlesString);
cv.put(NFCMLContent.Geeks.Columns.TIMEWINNER.getName(), 0);
final Uri rowUri = cr.insert(NFCMLContent.Geeks.CONTENT_URI, cv);
boolean success = rowUri != null;
if (success) {
// Push the updated DB file to Dropbox
DropboxHelper.pushDB(context);
}
if (DEBUG) Log.d(TAG, String.format("Contact added: %s (URI: %s)", success, rowUri));
return success;
}
/**
* Removes all heading and trailing separators ({@link #VALUES_SEPARATOR})
* from a list of values.
*
* @param valuesList The list of values to cleanup
*
* @return Returns the cleaned up list
*/
public static String cleanupSeparators(String valuesList) {
while (valuesList.startsWith(VALUES_SEPARATOR)) {
valuesList = valuesList.substring(1);
}
while (valuesList.endsWith(VALUES_SEPARATOR)) {
valuesList = valuesList.substring(0, valuesList.length() - 1);
}
return valuesList;
}
/**
* Packs a list of strings into a single string containing
* all those strings, separated by {@link #VALUES_SEPARATOR}.
* <p/>
* It will then be splittable by using {@link String#split(String)}.
* For example:
* <code>String[] items = packedString.split(DataHelper.VALUES_SEPARATOR);</code>
*
* @param list The list of strings to pack
*
* @return Returns the packed string (empty if the list is empty, or null)
*/
private static String packList(List<String> list) {
if (list == null || list.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (String item : list) {
if (item != null && TextUtils.isGraphic(item)) {
sb.append(item).append(VALUES_SEPARATOR);
}
}
if (sb.length() == 0) {
return "";
}
sb = sb.deleteCharAt(sb.length() - 1); // Remove trailing separator
return sb.toString();
}
/**
* Checks if the given email has already been inserted into database.
* This is used before inserting a new contact, in order to avoid duplicates.
*
* @param context The current context
* @param email The email to check for in the database
*
* @return Returns true if the given email is present, false otherwise
*/
public static boolean isEmailAlreadyPresent(Context context, String email) {
if (DEBUG) {
Log.v(TAG, String.format("Checking for duplicates in the DB. Email: '%s'", email));
}
if (TextUtils.isEmpty(email)) {
return false;
}
// Note that we surround each email with VALUES_SEPARATOR to avoid having
// the 'LIKE' SQL operator find subsets of emails in the DB and incorrectly
// report them as already used.
// For example, if the database contains the address cba@example.com, and
// we search for ba@example.com without the separators surrounding the query,
// we would be told that ba@example.com is already in the database, when
// it's clearly not. This is a problem with, say, Gmail, Yahoo! Mail, ...
ContentResolver cr = context.getContentResolver();
Cursor result = cr.query(
NFCMLContent.Geeks.CONTENT_URI,
NFCMLContent.Geeks.PROJECTION,
NFCMLContent.Geeks.Columns.EMAIL.getName() + " LIKE ?",
new String[] {"%" + VALUES_SEPARATOR + email + VALUES_SEPARATOR + "%"},
null
);
boolean found = result != null && result.getCount() > 0;
if (result != null) {
result.close();
}
if (DEBUG) Log.d(TAG, String.format("Email '%s' already present in DB: %s", email, found));
return found;
}
/**
* Update the "iswinner" field for the contact with the given email in
* the local Content Provider, if present.
*
* @param context The current context
* @param email The email address of the winner to update
* @param winnerValue The new "iswinner" value
*/
public static boolean markWinner(Context context, String email, boolean winnerValue) {
if (DEBUG) {
Log.v(TAG, String.format("Updating 'iswinner' value. Email: %s, value: %s", email, winnerValue));
}
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
// The winner has a non-zero timestamp (the current one); a non-winner has a zero timestamp
cv.put(NFCMLContent.Geeks.Columns.TIMEWINNER.getName(), winnerValue ? System.currentTimeMillis() : 0);
int updatedRecords = cr.update(NFCMLContent.Geeks.CONTENT_URI, cv,
NFCMLContent.Geeks.Columns.EMAIL.getName() + " LIKE ?",
new String[] {"%" + VALUES_SEPARATOR + email + VALUES_SEPARATOR + "%"});
if (updatedRecords > 0) {
// Push the updated DB file to Dropbox
DropboxHelper.pushDB(context);
if (updatedRecords > 1) {
Log.w(TAG, updatedRecords + " row(s) updated in a markWinner operation. Something's fishy!");
}
else if (DEBUG) {
Log.d(TAG, String.format("Winner status updated for email %s (isWinner: %s)", email, winnerValue));
}
}
else {
Log.w(TAG, "Unable to update winner status. Contact not in DB?");
}
return updatedRecords > 0;
}
/**
* Update the "iswinner" field for the contact related to the given email into the local Content Provider
*
* @param context The current context
* @param winnerId The ID of the winner to clear
*
* @return Returns the count of rows the victory was cleared for.
*/
public static int clearWinner(Context context, int winnerId) {
if (DEBUG) Log.v(TAG, String.format("Clearing 'iswinner' value. WinnerID: %d", winnerId));
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(NFCMLContent.Geeks.Columns.TIMEWINNER.getName(), 0);
int updatedRecords = cr.update(NFCMLContent.Geeks.CONTENT_URI, cv,
NFCMLContent.Geeks.Columns.ID.getName() + "=?",
new String[] {String.valueOf(winnerId)});
if (updatedRecords > 0) {
// Push the updated DB file to Dropbox
DropboxHelper.pushDB(context);
if (updatedRecords > 1) {
Log.w(TAG, updatedRecords + " row(s) updated in a clearWinner operation. Something's fishy!");
}
else if (DEBUG) {
Log.d(TAG, "Winner status cleared for ID " + winnerId);
}
}
else {
Log.w(TAG, "Unable to update winner status for ID " + winnerId + ". Contact not in DB?");
}
return updatedRecords;
}
/**
* Clears the "iswinner" field for all contacts in the database.
*
* @param context The current context
*
* @return Returns the count of rows the victory was cleared for
*/
public static int clearAllWinners(Context context) {
if (DEBUG) Log.v(TAG, String.format("Clearing 'iswinner' value for all contacts"));
ContentResolver cr = context.getContentResolver();
ContentValues cv = new ContentValues();
cv.put(NFCMLContent.Geeks.Columns.TIMEWINNER.getName(), 0);
int updatedRecords = cr.update(NFCMLContent.Geeks.CONTENT_URI, cv, null, null);
if (DEBUG) Log.d(TAG, "Cleared victory flag for all winners; updated " + updatedRecords + " record(s)");
return updatedRecords;
}
}