mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
682b1101a5
- today is from 00:00.00 this morning until the end of time - yesterday is from yesterday 00:00.00 until today - the other ones also mean pretty much exactly what you would expect them to mean. - this week and this month both can be empty because they are already covered by today, yesterday etc. This is by design.
262 lines
8.7 KiB
Java
262 lines
8.7 KiB
Java
package org.thoughtcrime.securesms.database.loaders;
|
|
|
|
|
|
import android.content.Context;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.v4.content.AsyncTaskLoader;
|
|
|
|
import com.annimon.stream.Stream;
|
|
import com.b44t.messenger.DcContext;
|
|
import com.b44t.messenger.DcMsg;
|
|
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.connect.DcHelper;
|
|
import org.thoughtcrime.securesms.database.Address;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Calendar;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMediaLoader.BucketedThreadMedia> {
|
|
|
|
@SuppressWarnings("unused")
|
|
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
|
|
|
|
private final Address address;
|
|
|
|
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull Address address) {
|
|
super(context);
|
|
this.address = address;
|
|
|
|
onContentChanged();
|
|
}
|
|
|
|
@Override
|
|
protected void onStartLoading() {
|
|
if (takeContentChanged()) {
|
|
forceLoad();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStopLoading() {
|
|
cancelLoad();
|
|
}
|
|
|
|
@Override
|
|
protected void onAbandon() {
|
|
}
|
|
|
|
@Override
|
|
public BucketedThreadMedia loadInBackground() {
|
|
BucketedThreadMedia result = new BucketedThreadMedia(getContext());
|
|
DcContext context = DcHelper.getContext(getContext());
|
|
int[] messages = context.getChatMedia(address.getDcChatId(), DcMsg.DC_MSG_IMAGE, DcMsg.DC_MSG_GIF, DcMsg.DC_MSG_VIDEO);
|
|
for(int nextId : messages) {
|
|
result.add(context.getMsg(nextId));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static class BucketedThreadMedia {
|
|
|
|
private final TimeBucket TODAY;
|
|
private final TimeBucket YESTERDAY;
|
|
private final TimeBucket THIS_WEEK;
|
|
private final TimeBucket LAST_WEEK;
|
|
private final TimeBucket THIS_MONTH;
|
|
private final TimeBucket LAST_MONTH;
|
|
private final MonthBuckets OLDER;
|
|
|
|
private final TimeBucket[] TIME_SECTIONS;
|
|
|
|
public BucketedThreadMedia(@NonNull Context context) {
|
|
// from today midnight until the end of human time
|
|
this.TODAY = new TimeBucket(context.getString(R.string.today),
|
|
addToCalendarFromTodayMidnight(Calendar.DAY_OF_YEAR, 0), Long.MAX_VALUE);
|
|
// from yesterday midnight until today midnight
|
|
this.YESTERDAY = new TimeBucket(context.getString(R.string.yesterday),
|
|
addToCalendarFromTodayMidnight(Calendar.DAY_OF_YEAR, -1), TODAY.startTime);
|
|
// from the closest start of week until yesterday midnight (that can be a negative timespace and thus be empty)
|
|
this.THIS_WEEK = new TimeBucket(context.getString(R.string.this_week),
|
|
setInCalendarFromTodayMidnight(Calendar.DAY_OF_WEEK, getCalendar().getFirstDayOfWeek()), YESTERDAY.startTime);
|
|
// from the closest start of week one week back until the closest start of week.
|
|
this.LAST_WEEK = new TimeBucket(context.getString(R.string.last_week),
|
|
addToCalendarFrom(THIS_WEEK.startTime, Calendar.WEEK_OF_YEAR, -1), THIS_WEEK.startTime);
|
|
// from the closest 1st of a month until one week prior to the closest start of week (can be negative and thus empty)
|
|
this.THIS_MONTH = new TimeBucket(context.getString(R.string.this_month),
|
|
setInCalendarFromTodayMidnight(Calendar.DAY_OF_MONTH, 1), LAST_WEEK.startTime);
|
|
// from the closest 1st of a month, one month back to the closest 1st of a month
|
|
this.LAST_MONTH = new TimeBucket(context.getString(R.string.last_month),
|
|
addToCalendarFrom(THIS_MONTH.startTime, Calendar.MONTH, -1), LAST_WEEK.startTime);
|
|
this.TIME_SECTIONS = new TimeBucket[]{TODAY, YESTERDAY, THIS_WEEK, LAST_WEEK, THIS_MONTH, LAST_MONTH};
|
|
this.OLDER = new MonthBuckets();
|
|
}
|
|
|
|
public void add(DcMsg imageMessage) {
|
|
for (TimeBucket timeSection : TIME_SECTIONS) {
|
|
if (timeSection.inRange(imageMessage.getTimestamp())) {
|
|
timeSection.add(imageMessage);
|
|
return;
|
|
}
|
|
}
|
|
OLDER.add(imageMessage);
|
|
}
|
|
|
|
public int getSectionCount() {
|
|
return (int)Stream.of(TIME_SECTIONS)
|
|
.filter(timeBucket -> !timeBucket.isEmpty())
|
|
.count() +
|
|
OLDER.getSectionCount();
|
|
}
|
|
|
|
public int getSectionItemCount(int section) {
|
|
List<TimeBucket> activeTimeBuckets = Stream.of(TIME_SECTIONS).filter(timeBucket -> !timeBucket.isEmpty()).toList();
|
|
|
|
if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getItemCount();
|
|
else return OLDER.getSectionItemCount(section - activeTimeBuckets.size());
|
|
}
|
|
|
|
public DcMsg get(int section, int item) {
|
|
List<TimeBucket> activeTimeBuckets = Stream.of(TIME_SECTIONS).filter(timeBucket -> !timeBucket.isEmpty()).toList();
|
|
|
|
if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getItem(item);
|
|
else return OLDER.getItem(section - activeTimeBuckets.size(), item);
|
|
}
|
|
|
|
public String getName(int section, Locale locale) {
|
|
List<TimeBucket> activeTimeBuckets = Stream.of(TIME_SECTIONS).filter(timeBucket -> !timeBucket.isEmpty()).toList();
|
|
|
|
if (section < activeTimeBuckets.size()) return activeTimeBuckets.get(section).getName();
|
|
else return OLDER.getName(section - activeTimeBuckets.size(), locale);
|
|
}
|
|
|
|
// tests should override this function to deliver a preset calendar.
|
|
Calendar getCalendar() {
|
|
return Calendar.getInstance();
|
|
}
|
|
|
|
long setInCalendarFromTodayMidnight(int field, int amount) {
|
|
Calendar calendar = getCalendar();
|
|
setCalendarToTodayMidnight(calendar);
|
|
calendar.set(field, amount);
|
|
return calendar.getTimeInMillis();
|
|
}
|
|
|
|
long addToCalendarFrom(long relativeTo, int field, int amount) {
|
|
Calendar calendar = getCalendar();
|
|
calendar.setTime(new Date(relativeTo));
|
|
calendar.add(field, amount);
|
|
return calendar.getTimeInMillis();
|
|
}
|
|
|
|
long addToCalendarFromTodayMidnight(int field, int amount) {
|
|
Calendar calendar = getCalendar();
|
|
setCalendarToTodayMidnight(calendar);
|
|
calendar.add(field, amount);
|
|
return calendar.getTimeInMillis();
|
|
}
|
|
|
|
void setCalendarToTodayMidnight(Calendar calendar) {
|
|
calendar.set(Calendar.HOUR_OF_DAY, 0);
|
|
calendar.set(Calendar.MINUTE, 0);
|
|
calendar.set(Calendar.SECOND, 0);
|
|
calendar.set(Calendar.MILLISECOND, 0);
|
|
}
|
|
|
|
private static class TimeBucket {
|
|
|
|
private final List<DcMsg> records = new LinkedList<>();
|
|
|
|
private final long startTime;
|
|
private final long endTime;
|
|
private final String name;
|
|
|
|
TimeBucket(String name, long startTime, long endTime) {
|
|
this.name = name;
|
|
this.startTime = startTime;
|
|
this.endTime = endTime;
|
|
}
|
|
|
|
void add(DcMsg record) {
|
|
this.records.add(record);
|
|
}
|
|
|
|
boolean inRange(long timestamp) {
|
|
return timestamp >= startTime && timestamp < endTime;
|
|
}
|
|
|
|
boolean isEmpty() {
|
|
return records.isEmpty();
|
|
}
|
|
|
|
int getItemCount() {
|
|
return records.size();
|
|
}
|
|
|
|
DcMsg getItem(int position) {
|
|
return records.get(position);
|
|
}
|
|
|
|
String getName() {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
private static class MonthBuckets {
|
|
|
|
private final Map<Date, List<DcMsg>> months = new HashMap<>();
|
|
|
|
void add(DcMsg record) {
|
|
Calendar calendar = Calendar.getInstance();
|
|
calendar.setTimeInMillis(record.getTimestamp());
|
|
|
|
int year = calendar.get(Calendar.YEAR) - 1900;
|
|
int month = calendar.get(Calendar.MONTH);
|
|
Date date = new Date(year, month, 1);
|
|
|
|
if (months.containsKey(date)) {
|
|
months.get(date).add(record);
|
|
} else {
|
|
List<DcMsg> list = new LinkedList<>();
|
|
list.add(record);
|
|
months.put(date, list);
|
|
}
|
|
}
|
|
|
|
int getSectionCount() {
|
|
return months.size();
|
|
}
|
|
|
|
int getSectionItemCount(int section) {
|
|
return months.get(getSection(section)).size();
|
|
}
|
|
|
|
DcMsg getItem(int section, int position) {
|
|
return months.get(getSection(section)).get(position);
|
|
}
|
|
|
|
Date getSection(int section) {
|
|
ArrayList<Date> keys = new ArrayList<>(months.keySet());
|
|
Collections.sort(keys, Collections.reverseOrder());
|
|
|
|
return keys.get(section);
|
|
}
|
|
|
|
String getName(int section, Locale locale) {
|
|
Date sectionDate = getSection(section);
|
|
|
|
return new SimpleDateFormat("MMMM yyyy", locale).format(sectionDate);
|
|
}
|
|
}
|
|
}
|
|
}
|