mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2024-09-20 13:59:08 -05:00
365 lines
16 KiB
Java
365 lines
16 KiB
Java
package pl.szczodrzynski.edziennik.fragments;
|
|
|
|
import androidx.appcompat.widget.PopupMenu;
|
|
import androidx.databinding.DataBindingUtil;
|
|
|
|
import android.graphics.Color;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.fragment.app.Fragment;
|
|
import androidx.core.graphics.ColorUtils;
|
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
|
|
import android.os.Handler;
|
|
import android.util.LongSparseArray;
|
|
import android.view.Gravity;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.Transformation;
|
|
import android.widget.Toast;
|
|
|
|
import com.danimahardhika.cafebar.CafeBar;
|
|
import com.mikepenz.iconics.IconicsColor;
|
|
import com.mikepenz.iconics.IconicsDrawable;
|
|
import com.mikepenz.iconics.IconicsSize;
|
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
|
|
|
import java.text.DecimalFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import antonkozyriatskyi.circularprogressindicator.CircularProgressIndicator;
|
|
import pl.szczodrzynski.edziennik.App;
|
|
import pl.szczodrzynski.edziennik.R;
|
|
import pl.szczodrzynski.edziennik.MainActivity;
|
|
import pl.szczodrzynski.edziennik.adapters.AttendancesAdapter;
|
|
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterAttendancesBinding;
|
|
import pl.szczodrzynski.edziennik.datamodels.AttendanceFull;
|
|
import pl.szczodrzynski.edziennik.datamodels.Subject;
|
|
import pl.szczodrzynski.edziennik.models.Date;
|
|
import pl.szczodrzynski.edziennik.models.Week;
|
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
|
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
|
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_ABSENT;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_ABSENT_EXCUSED;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_BELATED;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_BELATED_EXCUSED;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_PRESENT;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_RELEASED;
|
|
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
|
|
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_VULCAN;
|
|
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_ATTENDANCE;
|
|
|
|
public class RegisterAttendancesFragment extends Fragment {
|
|
|
|
private App app = null;
|
|
private MainActivity activity = null;
|
|
private FragmentRegisterAttendancesBinding b = null;
|
|
|
|
private int displayMode = MODE_YEAR;
|
|
private static final int MODE_YEAR = 0;
|
|
private static final int MODE_SEMESTER_1 = 1;
|
|
private static final int MODE_SEMESTER_2 = 2;
|
|
private long subjectIdFilter = -1;
|
|
private LongSparseArray<int[]> subjectTotalCount;
|
|
private LongSparseArray<int[]> subjectAbsentCount;
|
|
private LongSparseArray<Float> subjectAttendancePercentage;
|
|
|
|
private List<AttendanceFull> attendanceList = null;
|
|
|
|
@Override
|
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
activity = (MainActivity) getActivity();
|
|
if (getActivity() == null || getContext() == null)
|
|
return null;
|
|
app = (App) activity.getApplication();
|
|
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
|
|
if (app.profile == null)
|
|
return inflater.inflate(R.layout.fragment_loading, container, false);
|
|
// activity, context and profile is valid
|
|
b = DataBindingUtil.inflate(inflater, R.layout.fragment_register_attendances, container, false);
|
|
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
|
|
return b.getRoot();
|
|
}
|
|
|
|
@Override
|
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
|
return;
|
|
|
|
activity.getBottomSheet().prependItems(
|
|
new BottomSheetPrimaryItem(true)
|
|
.withTitle(R.string.menu_mark_as_read)
|
|
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
|
.withOnClickListener(v3 -> {
|
|
activity.getBottomSheet().close();
|
|
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true));
|
|
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
|
|
})
|
|
);
|
|
|
|
/*b.refreshLayout.setOnRefreshListener(() -> {
|
|
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_ATTENDANCES, b.refreshLayout);
|
|
});*/
|
|
|
|
b.attendancePercentage.setProgressTextAdapter(PERCENTAGE_ADAPTER);
|
|
b.attendancePercentage.setMaxProgress(100.0f);
|
|
|
|
b.attendancesSummaryTitle.setOnClickListener((v -> {
|
|
PopupMenu popupMenu = new PopupMenu(activity, b.attendancesSummaryTitle, Gravity.END);
|
|
popupMenu.getMenu().add(0, 0, 0, R.string.summary_mode_year);
|
|
popupMenu.getMenu().add(0, 1, 1, R.string.summary_mode_semester_1);
|
|
popupMenu.getMenu().add(0, 2, 2, R.string.summary_mode_semester_2);
|
|
popupMenu.setOnMenuItemClickListener((item -> {
|
|
displayMode = item.getItemId();
|
|
updateList();
|
|
return true;
|
|
}));
|
|
popupMenu.show();
|
|
}));
|
|
|
|
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK) {
|
|
long attendancesLastSync = app.profile.getStudentData("attendancesLastSync", (long)0);
|
|
if (attendancesLastSync == 0) {
|
|
attendancesLastSync = app.profile.getSemesterStart(1).getInMillis();
|
|
}
|
|
Date lastSyncDate = Date.fromMillis(attendancesLastSync);
|
|
if (lastSyncDate.getValue() < Week.getWeekStart().getValue()) {
|
|
CafeBar.builder(activity)
|
|
.to(activity.getNavView().getCoordinator())
|
|
.content(R.string.sync_old_data_info)
|
|
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_sync).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
|
|
.positiveText(R.string.refresh)
|
|
.positiveColor(0xff4caf50)
|
|
.negativeText(R.string.ok)
|
|
.negativeColor(0x66ffffff)
|
|
.onPositive((cafeBar -> {
|
|
if (!activity.getSwipeRefreshLayout().isRefreshing()) {
|
|
cafeBar.dismiss();
|
|
activity.syncCurrentFeature();
|
|
}
|
|
else {
|
|
Toast.makeText(app, R.string.please_wait, Toast.LENGTH_SHORT).show();
|
|
}
|
|
}))
|
|
.onNegative(CafeBar::dismiss)
|
|
.autoDismiss(false)
|
|
.swipeToDismiss(true)
|
|
.floating(true)
|
|
.show();
|
|
}
|
|
}
|
|
|
|
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && false) {
|
|
b.attendancesSummarySubject.setVisibility(View.GONE);
|
|
}
|
|
else {
|
|
b.attendancesSummarySubject.setOnClickListener((v -> {
|
|
AsyncTask.execute(() -> {
|
|
List<Subject> subjectList = app.db.subjectDao().getAllNow(App.profileId);
|
|
PopupMenu popupMenu = new PopupMenu(activity, b.attendancesSummarySubject, Gravity.END);
|
|
popupMenu.getMenu().add(0, -1, 0, R.string.subject_filter_disabled);
|
|
int index = 0;
|
|
DecimalFormat format = new DecimalFormat("0.00");
|
|
for (Subject subject: subjectList) {
|
|
int total = subjectTotalCount.get(subject.id, new int[3])[displayMode];
|
|
int absent = subjectAbsentCount.get(subject.id, new int[3])[displayMode];
|
|
if (total == 0)
|
|
continue;
|
|
int present = total - absent;
|
|
float percentage = (float)present / (float)total * 100.0f;
|
|
String percentageStr = format.format(percentage);
|
|
popupMenu.getMenu().add(0, (int)subject.id, index++, getString(R.string.subject_filter_format, subject.longName, percentageStr));
|
|
}
|
|
popupMenu.setOnMenuItemClickListener((item -> {
|
|
subjectIdFilter = item.getItemId();
|
|
b.attendancesSummarySubject.setText(item.getTitle().toString().replaceAll("\\s-\\s[0-9]{1,2}\\.[0-9]{1,2}%", ""));
|
|
updateList();
|
|
return true;
|
|
}));
|
|
new Handler(activity.getMainLooper()).post(popupMenu::show);
|
|
});
|
|
|
|
}));
|
|
}
|
|
|
|
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
|
|
|
|
b.attendancesView.setHasFixedSize(true);
|
|
b.attendancesView.setLayoutManager(linearLayoutManager);
|
|
|
|
app.db.attendanceDao().getAll(App.profileId).observe(this, attendances -> {
|
|
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
|
return;
|
|
|
|
if (attendances == null) {
|
|
b.attendancesView.setVisibility(View.GONE);
|
|
b.attendancesNoData.setVisibility(View.VISIBLE);
|
|
return;
|
|
}
|
|
|
|
attendanceList = attendances;
|
|
|
|
countSubjectStats();
|
|
|
|
updateList();
|
|
});
|
|
}
|
|
|
|
private void countSubjectStats() {
|
|
subjectTotalCount = new LongSparseArray<>();
|
|
subjectAbsentCount = new LongSparseArray<>();
|
|
for (AttendanceFull attendance: attendanceList) {
|
|
if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.type == TYPE_RELEASED)
|
|
continue;
|
|
int[] subjectTotal = subjectTotalCount.get(attendance.subjectId, new int[3]);
|
|
int[] subjectAbsent = subjectAbsentCount.get(attendance.subjectId, new int[3]);
|
|
|
|
subjectTotal[0]++;
|
|
subjectTotal[attendance.semester]++;
|
|
|
|
if (attendance.type == TYPE_ABSENT || attendance.type == TYPE_ABSENT_EXCUSED) {
|
|
subjectAbsent[0]++;
|
|
subjectAbsent[attendance.semester]++;
|
|
}
|
|
|
|
subjectTotalCount.put(attendance.subjectId, subjectTotal);
|
|
subjectAbsentCount.put(attendance.subjectId, subjectAbsent);
|
|
}
|
|
}
|
|
|
|
private void updateList() {
|
|
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
|
return;
|
|
|
|
int presentCount = 0;
|
|
int absentCount = 0;
|
|
int absentUnexcusedCount = 0;
|
|
int belatedCount = 0;
|
|
int releasedCount = 0;
|
|
|
|
List<AttendanceFull> filteredList = new ArrayList<>();
|
|
for (AttendanceFull attendance: attendanceList) {
|
|
if (displayMode != MODE_YEAR && attendance.semester != displayMode)
|
|
continue;
|
|
if (subjectIdFilter != -1 && attendance.subjectId != subjectIdFilter)
|
|
continue;
|
|
if (attendance.type != TYPE_PRESENT)
|
|
filteredList.add(attendance);
|
|
switch (attendance.type) {
|
|
case TYPE_PRESENT:
|
|
presentCount++;
|
|
break;
|
|
case TYPE_ABSENT:
|
|
absentCount++;
|
|
absentUnexcusedCount++;
|
|
break;
|
|
case TYPE_ABSENT_EXCUSED:
|
|
absentCount++;
|
|
break;
|
|
case TYPE_BELATED_EXCUSED:
|
|
case TYPE_BELATED:
|
|
belatedCount++;
|
|
break;
|
|
case TYPE_RELEASED:
|
|
releasedCount++;
|
|
break;
|
|
}
|
|
}
|
|
if (filteredList.size() > 0) {
|
|
AttendancesAdapter adapter;
|
|
b.attendancesView.setVisibility(View.VISIBLE);
|
|
b.attendancesNoData.setVisibility(View.GONE);
|
|
if ((adapter = (AttendancesAdapter) b.attendancesView.getAdapter()) != null) {
|
|
adapter.attendanceList = filteredList;
|
|
adapter.notifyDataSetChanged();
|
|
}
|
|
else {
|
|
adapter = new AttendancesAdapter(getContext(), filteredList);
|
|
b.attendancesView.setAdapter(adapter);
|
|
}
|
|
}
|
|
else {
|
|
b.attendancesView.setVisibility(View.GONE);
|
|
b.attendancesNoData.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
// SUMMARY
|
|
if (displayMode == MODE_YEAR) {
|
|
b.attendancesSummaryTitle.setText(getString(R.string.attendances_summary_title_year));
|
|
}
|
|
else {
|
|
b.attendancesSummaryTitle.setText(getString(R.string.attendances_summary_title_semester_format, displayMode));
|
|
}
|
|
b.presentCountContainer.setVisibility(presentCount == 0 ? View.GONE : View.VISIBLE);
|
|
b.presentCount.setText(String.format(Locale.getDefault(), "%d", presentCount));
|
|
b.absentCount.setText(String.format(Locale.getDefault(), "%d", absentCount));
|
|
b.absentUnexcusedCount.setText(String.format(Locale.getDefault(), "%d", absentUnexcusedCount));
|
|
b.belatedCount.setText(String.format(Locale.getDefault(), "%d", belatedCount));
|
|
b.releasedCount.setText(String.format(Locale.getDefault(), "%d", releasedCount));
|
|
if (absentUnexcusedCount >= 5) {
|
|
b.absentUnexcusedCount.setTextColor(Color.RED);
|
|
}
|
|
else {
|
|
b.absentUnexcusedCount.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
|
|
}
|
|
|
|
float attendancePercentage;
|
|
|
|
// in Mobidziennik there are no TYPE_PRESENT records so we cannot calculate the percentage
|
|
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && false) {
|
|
attendancePercentage = app.profile.getAttendancePercentage();
|
|
}
|
|
else if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN) {
|
|
float allCount = presentCount + absentCount + belatedCount; // do not count releases
|
|
float present = allCount - absentCount;
|
|
attendancePercentage = present / allCount * 100.0f;
|
|
}
|
|
else {
|
|
float allCount = presentCount + absentCount + belatedCount + releasedCount;
|
|
float present = allCount - absentCount;
|
|
attendancePercentage = present / allCount * 100.0f;
|
|
}
|
|
// if it's still 0%, hide the indicator
|
|
if (attendancePercentage <= 0.0f) {
|
|
b.attendancePercentage.setVisibility(View.GONE);
|
|
return;
|
|
}
|
|
animatePercentageIndicator(attendancePercentage);
|
|
}
|
|
|
|
private void animatePercentageIndicator(float percentage) {
|
|
Animation a = new Animation() {
|
|
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
|
float progress = percentage *interpolatedTime;
|
|
if (interpolatedTime == 1.0f) {
|
|
progress = percentage;
|
|
}
|
|
int color = ColorUtils.blendARGB(Color.RED, Color.GREEN, progress/100.0f);
|
|
b.attendancePercentage.setTextColor(color);
|
|
b.attendancePercentage.setProgressColor(color);
|
|
b.attendancePercentage.setCurrentProgress(progress);
|
|
}
|
|
public boolean willChangeBounds() {
|
|
return true;
|
|
}
|
|
};
|
|
a.setDuration(1300);
|
|
a.setInterpolator(new AccelerateDecelerateInterpolator());
|
|
b.attendancePercentage.postDelayed(() -> b.attendancePercentage.startAnimation(a), 500);
|
|
}
|
|
|
|
private static final CircularProgressIndicator.ProgressTextAdapter PERCENTAGE_ADAPTER = value -> {
|
|
DecimalFormat df = new DecimalFormat("0.##");
|
|
return df.format(value)+"%";
|
|
};
|
|
}
|