Java tutorial
/* * * Copyright 2015, Jose Calles * * 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 com.josecalles.tistiq.task; import android.os.AsyncTask; import com.josecalles.tistiq.TistiqApplication; import com.josecalles.tistiq.event.SyncCompleteEvent; import com.josecalles.tistiq.object.Contact; import com.josecalles.tistiq.object.StatSettings; import com.josecalles.tistiq.object.Statistic; import com.josecalles.tistiq.object.TextMessage; import com.josecalles.tistiq.type.StatType; import de.greenrobot.event.EventBus; import io.realm.Realm; import io.realm.RealmList; import io.realm.RealmResults; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.joda.time.DateTime; import org.joda.time.Duration; public final class StatCalculator { private final static String CLEAN_MSG_BODY_REGEX = "[^A-Za-z&&[^']]"; private final static String LAUGH_REGEX = "lol|haha|hehe|jaja|lma[o+]"; private long mReplyCutOffTime; private int mValidWordLength; private int mNumberOfMostUsed; private TistiqApplication mApplication; public StatCalculator(TistiqApplication application) { mApplication = application; } public void fetchStatistics(StatSettings settings) { setSettings(settings); new CalculateStatsTask().execute(); } private void setSettings(StatSettings settings) { mReplyCutOffTime = settings.getReplyCutOffTime(); mValidWordLength = settings.getValidWordLength(); mNumberOfMostUsed = settings.getNumberOfMostUsed(); } public int getAverageLength(RealmList<TextMessage> messages) { int sumOfWords = 0; for (TextMessage message : messages) { String messageBody = message.getMessageBody(); String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim(); int numberOfWords = cleanMessageBody.isEmpty() ? 0 : cleanMessageBody.split("\\s+").length; sumOfWords += numberOfWords; } return sumOfWords / messages.size(); } public String getMostUsedWords(RealmList<TextMessage> messages) { ArrayList<String> words = new ArrayList<>(); ArrayList<String> results; StringBuilder stringBuilder = new StringBuilder(); Map<String, Integer> wordsWithCount = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (int i = 0; i < messages.size(); i++) { String messageBody = messages.get(i).getMessageBody(); String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim(); String[] wordsInMessage = cleanMessageBody.split("\\s+"); for (int j = 0; j < wordsInMessage.length; j++) { if (wordsInMessage[j].length() > mValidWordLength) words.add(wordsInMessage[j]); } } for (int i = 0; i < words.size(); i++) { if (wordsWithCount.containsKey(words.get(i))) { wordsWithCount.put(words.get(i), (wordsWithCount.get(words.get(i)) + 1)); } else { wordsWithCount.put(words.get(i), 1); } } results = sortValues(wordsWithCount, mNumberOfMostUsed, false); if (results.size() == 0) return "N/A"; for (int i = 0; i < results.size(); i++) { if (i == mNumberOfMostUsed - 1) { stringBuilder.append(results.get(i)); } else { stringBuilder.append(results.get(i) + " "); } } return stringBuilder.toString(); } public static <K, V extends Comparable<? super V>> ArrayList<String> sortValues(Map<K, V> map, int numberOfResults, boolean getValue) { ArrayList<String> results = new ArrayList<>(); List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet()); Collections.sort(list, new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) { return (o2.getValue()).compareTo(o1.getValue()); } }); Map<K, V> result = new LinkedHashMap<>(); for (Map.Entry<K, V> entry : list) { result.put(entry.getKey(), entry.getValue()); } if (getValue) { for (Map.Entry<K, V> entry : result.entrySet()) { Integer value = ((Integer) entry.getValue()); String stringValue = Integer.toString(value); results.add(stringValue); return results; } } boolean done = false; for (Map.Entry<K, V> entry : result.entrySet()) { if (!done) { results.add((String) entry.getKey()); if (results.size() == result.entrySet().size()) { done = true; } else { done = (results.size() == numberOfResults); } } } return results; } public String getWhatDayMostMessagesSent(RealmList<TextMessage> messages) { ArrayList<String> dateTimestamps = new ArrayList<>(); for (TextMessage message : messages) { DateTime time = new DateTime(message.getDateReceived()); String dayOfWeek = time.dayOfWeek().getAsText(Locale.ENGLISH); dateTimestamps.add(dayOfWeek); } HashMap<String, Integer> daysWithCount = new HashMap<>(); for (int i = 0; i < dateTimestamps.size(); i++) { if (daysWithCount.containsKey(dateTimestamps.get(i))) { daysWithCount.put(dateTimestamps.get(i), (daysWithCount.get(dateTimestamps.get(i)) + 1)); } else { daysWithCount.put(dateTimestamps.get(i), 1); } } String dayOnWhich = sortValues(daysWithCount, 1, false).get(0); return dayOnWhich + "s"; } public long timeItTakesToReplySelf(RealmList<TextMessage> messagesReceived, RealmList<TextMessage> messagesSent) { if (messagesReceived.size() == 0) { return -1; } long totalTime = 0; long count = 0; for (TextMessage sentMessage : messagesSent) { for (TextMessage receivedMessage : messagesReceived) { if (receivedMessage.getDateReceived() > sentMessage.getDateReceived()) { if (receivedMessage.getDateReceived() - sentMessage.getDateReceived() < mReplyCutOffTime) { totalTime += (receivedMessage.getDateReceived() - sentMessage.getDateReceived()); count += 1; break; } } } } if (count == 0) return -1; return totalTime / count; } public long timeItTakesToReplyContact(RealmList<TextMessage> messagesReceived, RealmList<TextMessage> messagesSent) { if (messagesSent.size() == 0) { return -1; } long totalTime = 0; long count = 0; for (TextMessage receivedMessage : messagesReceived) { for (TextMessage sentMessage : messagesSent) { if (sentMessage.getDateReceived() > receivedMessage.getDateReceived()) { if (sentMessage.getDateReceived() - receivedMessage.getDateReceived() < mReplyCutOffTime) { totalTime += (sentMessage.getDateReceived() - receivedMessage.getDateReceived()); count += 1; break; } } } } if (count == 0) return -1; return totalTime / count; } public long timeSinceLastMessage(RealmList<TextMessage> messages) { long time = messages.get(messages.size() - 1).getDateReceived(); DateTime lastMessageTime = new DateTime(time); DateTime currentTime = new DateTime(); Duration duration = new Duration(lastMessageTime, currentTime); return duration.getMillis(); } public int mostMessagesSentInADay(RealmList<TextMessage> messages) { ArrayList<String> daysOfYear = new ArrayList<>(); for (TextMessage message : messages) { DateTime time = new DateTime(message.getDateReceived()); String dayOfYear = Integer.toString(time.getDayOfYear()); daysOfYear.add(dayOfYear); } HashMap<String, Integer> daysWithCount = new HashMap<>(); for (int i = 0; i < daysOfYear.size(); i++) { if (daysWithCount.containsKey(daysOfYear.get(i))) { daysWithCount.put(daysOfYear.get(i), (daysWithCount.get(daysOfYear.get(i)) + 1)); } else { daysWithCount.put(daysOfYear.get(i), 1); } } String numberOfMessages = sortValues(daysWithCount, 1, true).get(0); return Integer.parseInt(numberOfMessages); } public int mostMessagesReceivedInADay(RealmList<TextMessage> messages) { ArrayList<String> daysOfYear = new ArrayList<>(); for (TextMessage message : messages) { DateTime time = new DateTime(message.getDateReceived()); String dayOfYear = Integer.toString(time.getDayOfYear()); daysOfYear.add(dayOfYear); } HashMap<String, Integer> daysWithCount = new HashMap<>(); for (int i = 0; i < daysOfYear.size(); i++) { if (daysWithCount.containsKey(daysOfYear.get(i))) { daysWithCount.put(daysOfYear.get(i), (daysWithCount.get(daysOfYear.get(i)) + 1)); } else { daysWithCount.put(daysOfYear.get(i), 1); } } String numberOfMessages = sortValues(daysWithCount, 1, true).get(0); return Integer.parseInt(numberOfMessages); } public int numberOfQuestionsAsked(RealmList<TextMessage> messages) { String regex = "[?]+"; Pattern pattern = Pattern.compile(regex); int count = 0; for (TextMessage message : messages) { Matcher matcher = pattern.matcher(message.getMessageBody()); while (matcher.find()) { count += 1; } } return count; } public int numberOfLaughs(RealmList<TextMessage> messages) { Pattern pattern = Pattern.compile(LAUGH_REGEX, Pattern.CASE_INSENSITIVE); int count = 0; for (TextMessage message : messages) { Matcher matcher = pattern.matcher(message.getMessageBody()); while (matcher.find()) { count += 1; } } return count; } public int getSentToAverageLength(RealmList<TextMessage> messages) { int sumOfWords = 0; for (int i = 0; i < messages.size(); i++) { String messageBody = messages.get(i).getMessageBody(); String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim(); int numberOfWords = cleanMessageBody.isEmpty() ? 0 : cleanMessageBody.split("\\s+").length; sumOfWords += numberOfWords; } if (messages.size() == 0) return 0; return sumOfWords / messages.size(); } public int sentToNumberOfLaughs(RealmList<TextMessage> messages) { Pattern pattern = Pattern.compile(LAUGH_REGEX, Pattern.CASE_INSENSITIVE); int count = 0; for (TextMessage message : messages) { Matcher matcher = pattern.matcher(message.getMessageBody()); while (matcher.find()) { count += 1; } } return count; } private class CalculateStatsTask extends AsyncTask<Void, Void, Void> { private RealmResults<Contact> mContactList; private Contact mSelfContact; private EventBus mEventBus; public CalculateStatsTask() { mEventBus = EventBus.getDefault(); } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { Realm realm = Realm.getInstance(mApplication); RealmList<TextMessage> sentMessages; RealmList<TextMessage> receivedMessages; mContactList = realm.where(Contact.class).notEqualTo("uniqueID", 0).findAll(); mSelfContact = realm.where(Contact.class).equalTo("uniqueID", 0).findFirst(); realm.beginTransaction(); realm.where(Statistic.class).findAll().clear(); for (int i = 0; i < mContactList.size(); i++) { if (mContactList.get(i).getSentMessages().size() != 0 && mContactList.get(i).getReceivedMessages().size() != 0) { Statistic statistic = new Statistic(); sentMessages = mContactList.get(i).getSentMessages(); receivedMessages = mContactList.get(i).getReceivedMessages(); statistic.setContactID(mContactList.get(i).getUniqueID()); statistic.setStatType(StatType.CONTACT); statistic.setContactName(mContactList.get(i).getDisplayName()); statistic.setMessagesSent(sentMessages.size()); statistic.setMessagesReceived(receivedMessages.size()); statistic.setAvgLengthOfMessage(getAverageLength(sentMessages)); statistic.setAvgReplyTimeInMillis(timeItTakesToReplyContact(receivedMessages, sentMessages)); statistic.setMostUsedWords(getMostUsedWords(sentMessages)); statistic.setDayMostMessagesSent(getWhatDayMostMessagesSent(sentMessages)); statistic.setMostSentInADay(mostMessagesSentInADay(sentMessages)); statistic.setTimeSinceLastMessage(timeSinceLastMessage(sentMessages)); statistic.setNumberOfQuestionsAsked(numberOfQuestionsAsked(sentMessages)); statistic.setNumberOfLaughs(numberOfLaughs(sentMessages)); statistic.setSentToAvgLength(getSentToAverageLength(receivedMessages)); statistic.setSentToReplyTimeInMillis(timeItTakesToReplySelf(receivedMessages, sentMessages)); statistic.setSentToNumberOfLaughs(sentToNumberOfLaughs(receivedMessages)); realm.copyToRealm(statistic); } } sentMessages = mSelfContact.getSentMessages(); receivedMessages = mSelfContact.getReceivedMessages(); long selfAvgReplyTime = (long) realm.where(Statistic.class).equalTo("statType", StatType.CONTACT) .averageInt("sentToReplyTimeInMillis"); Statistic statistic = new Statistic(); statistic.setContactID(0); statistic.setStatType(StatType.SELF); statistic.setMessagesSent(sentMessages.size()); statistic.setMessagesReceived(receivedMessages.size()); statistic.setMostUsedWords(getMostUsedWords(sentMessages)); statistic.setAvgLengthOfMessage(getAverageLength(sentMessages)); statistic.setAvgReplyTimeInMillis(selfAvgReplyTime); statistic.setDayMostMessagesSent(getWhatDayMostMessagesSent(sentMessages)); statistic.setMostSentInADay(mostMessagesSentInADay(sentMessages)); statistic.setMostReceivedInADay(mostMessagesReceivedInADay(receivedMessages)); statistic.setNumberOfQuestionsAsked(numberOfQuestionsAsked(sentMessages)); realm.copyToRealm(statistic); realm.commitTransaction(); realm.close(); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); mEventBus.post(new SyncCompleteEvent()); } } }