[Structure] Move fragments, activities and adapters to specific modules

This commit is contained in:
Kacper Ziubryniewicz
2019-09-28 14:37:05 +02:00
parent 2d0f94a3a2
commit 165a804ba9
137 changed files with 333 additions and 400 deletions

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.dialogs;
import android.content.Context;
import android.view.View;
import com.afollestad.materialdialogs.MaterialDialog;
import pl.szczodrzynski.edziennik.App;
public class GenericDialog {
private App app;
private Context context;
public GenericDialog(Context context) {
this.context = context;
}
private MaterialDialog dialog;
private View dialogView;
public void positiveButton()
{
}
public void show(App _app)
{
this.app = _app;
dialog = new MaterialDialog.Builder(context)
.title("Dialog title")
//.customView(R.layout.dialog_register_exam_add, true)
.positiveText("Positive")
.negativeText("Negative")
.autoDismiss(false)
.onPositive((dialog, which) -> positiveButton())
.onNegative((dialog, which) -> dialog.dismiss())
.show();
dialogView = dialog.getCustomView();
assert dialogView != null;
// USE NavView.TextView.* instead
/*TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
@ColorInt int primaryTextColor = typedValue.data;*/
}
}

View File

@ -0,0 +1,92 @@
package pl.szczodrzynski.edziennik.ui.dialogs.changelog;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.utils.Themes;
import static pl.szczodrzynski.edziennik.utils.Utils.hexFromColorInt;
import static pl.szczodrzynski.navlib.UtilsKt.getColorFromAttr;
public class ChangelogDialog extends DialogFragment {
private String charsetName = "UTF-8";
private int getColor(int resId)
{
return getContext().getResources().getColor(resId);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
App app = (App)getContext().getApplicationContext();
final View customView;
try {
customView = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_changelog, null);
} catch (InflateException e) {
Toast.makeText(getContext(), "This device does not support WebViews.", Toast.LENGTH_SHORT).show();
return null;
}
boolean darkTheme = Themes.INSTANCE.isDark();
MaterialDialog dialog =
new MaterialDialog.Builder(getActivity())
.theme(darkTheme ? Theme.DARK : Theme.LIGHT)
.title(R.string.whats_new)
.customView(customView, true)
.positiveText(android.R.string.ok)
.build();
WebView webView = customView.findViewById(R.id.webview);
try {
// Load from changelog.html in the assets folder
StringBuilder buf = new StringBuilder();
InputStream json = getActivity().getAssets().open(getContext().getString(R.string.prefix)+"-changelog.html");
BufferedReader in = new BufferedReader(new InputStreamReader(json, charsetName));
String str;
while ((str = in.readLine()) != null) {
buf.append(str);
}
in.close();
// Inject color values for WebView body background and links
webView.loadDataWithBaseURL(null,
buf.toString()
.replace("{bg-color}", hexFromColorInt(darkTheme ? 0x424242 : 0xffffff))
.replace("{text-color}", colorToHex(getColorFromAttr(getContext(), android.R.attr.textColorPrimary)))
.replace("{secondary-color}", colorToHex(getColorFromAttr(getContext(), android.R.attr.textColorSecondary)))
.replace("{link-color}", colorToHex(getColorFromAttr(getContext(), R.attr.colorAccent)))
.replace("{link-color-active}", colorToHex(getColorFromAttr(getContext(), R.attr.colorPrimaryDark))),
"text/html",
charsetName, null);
} catch (Throwable e) {
webView.loadData(
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", charsetName);
}
return dialog;
}
private String colorToHex(int color) {
return Integer.toHexString(color).substring(2);
}
}

View File

@ -0,0 +1,133 @@
package pl.szczodrzynski.edziennik.ui.dialogs.event;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.mikepenz.iconics.view.IconicsImageView;
import com.mikepenz.iconics.view.IconicsTextView;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.Event;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
private static final String TAG = "EventListAdapter";
private Context context;
private List<EventFull> examList;
private EventListDialog parentDialog;
//getting the context and product list with constructor
public EventListAdapter(Context mCtx, List<EventFull> examList, EventListDialog parentDialog) {
this.context = mCtx;
this.examList = examList;
this.parentDialog = parentDialog;
}
@NonNull
@Override
public EventListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_dialog_event_list_item, parent, false);
return new EventListAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull EventListAdapter.ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
EventFull event = examList.get(position);
if (event.type == TYPE_HOMEWORK) {
holder.eventListItemRoot.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
}
else {
holder.eventListItemRoot.getBackground().setColorFilter(new PorterDuffColorFilter(event.getColor(), PorterDuff.Mode.MULTIPLY));
}
holder.eventListItemStartTime.setText(event.startTime == null ? app.getString(R.string.event_all_day) : event.startTime.getStringHM());
//holder.examListItemEndTime.setText(event.endTime.getStringHM());
holder.eventListItemTeamName.setText(Utils.bs(event.teamName));
holder.eventListItemTeacherName.setText(bs(null, event.teacherFullName, "\n") + bs(event.subjectLongName));
holder.eventListItemAddedDate.setText(Date.fromMillis(event.addedDate).getFormattedStringShort());
holder.eventListItemType.setText(event.typeName);
holder.eventListItemTopic.setText(event.topic);
holder.eventListItemEdit.setVisibility((event.addedManually ? View.VISIBLE : View.GONE));
holder.eventListItemHomework.setVisibility((event.type == Event.TYPE_HOMEWORK ? View.VISIBLE : View.GONE));
holder.eventListItemEdit.setOnClickListener(v -> {
if (parentDialog != null) {
parentDialog.callDismissListener = false;
parentDialog.dialog.dismiss();
}
d(TAG, "Event "+event);
if (event.type == Event.TYPE_HOMEWORK) {
new EventManualDialog(context, event.profileId)
.withDismissListener(parentDialog::performDismiss)
.show(app, event, null, null, EventManualDialog.DIALOG_HOMEWORK);
}
else {
new EventManualDialog(context, event.profileId)
.withDismissListener(parentDialog::performDismiss)
.show(app, event, null, null, EventManualDialog.DIALOG_EVENT);
}
});
if (event.sharedBy == null) {
holder.eventListItemSharedBy.setVisibility(View.GONE);
}
else if (event.sharedByName != null) {
holder.eventListItemSharedBy.setText(app.getString(R.string.event_shared_by_format, (event.sharedBy.equals("self") ? app.getString(R.string.event_shared_by_self) : event.sharedByName)));
}
}
@Override
public int getItemCount() {
return examList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
ConstraintLayout eventListItemRoot;
TextView eventListItemStartTime;
TextView eventListItemTeacherName;
TextView eventListItemType;
TextView eventListItemTopic;
TextView eventListItemAddedDate;
TextView eventListItemTeamName;
IconicsImageView eventListItemEdit;
IconicsImageView eventListItemHomework;
IconicsTextView eventListItemSharedBy;
ViewHolder(View itemView) {
super(itemView);
eventListItemRoot = itemView.findViewById(R.id.eventListItemRoot);
eventListItemStartTime = itemView.findViewById(R.id.eventListItemStartTime);
eventListItemTeacherName = itemView.findViewById(R.id.eventListItemTeacherName);
eventListItemType = itemView.findViewById(R.id.eventListItemType);
eventListItemTopic = itemView.findViewById(R.id.eventListItemTopic);
eventListItemAddedDate = itemView.findViewById(R.id.eventListItemAddedDate);
eventListItemTeamName = itemView.findViewById(R.id.eventListItemTeamName);
eventListItemEdit = itemView.findViewById(R.id.eventListItemEdit);
eventListItemHomework = itemView.findViewById(R.id.eventListItemHomework);
eventListItemSharedBy = itemView.findViewById(R.id.eventListItemSharedBy);
}
}
}

View File

@ -0,0 +1,203 @@
package pl.szczodrzynski.edziennik.ui.dialogs.event;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.LessonChange;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
public class EventListDialog {
private App app;
private Context context;
private int profileId;
public EventListDialog(Context context) {
this.context = context;
this.profileId = App.profileId;
}
public EventListDialog(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
public MaterialDialog dialog;
private View dialogView;
private RecyclerView examsView;
private DialogInterface.OnDismissListener dismissListener;
public boolean callDismissListener = true;
private LessonFull lesson;
public EventListDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
return this;
}
public void performDismiss(DialogInterface dialogInterface) {
if (callDismissListener && dismissListener != null) {
dismissListener.onDismiss(dialogInterface);
}
callDismissListener = true;
}
public void show(App _app, Date date)
{
show(_app, date, null);
}
public void show(App _app, Date date, Time time) {
show(_app, date, time, false);
}
public void show(App _app, Date date, Time time, boolean noDefaultTimeWhenAdding) {
if (!(context instanceof AppCompatActivity) || date == null)
return;
this.app = _app;
if (time != null) {
AsyncTask.execute(() -> {
this.lesson = app.db.lessonDao().getByDateTimeNow(profileId, date, time);
((AppCompatActivity) context).runOnUiThread(() -> {
actualShow(date, time, noDefaultTimeWhenAdding);
});
});
return;
}
actualShow(date, null, noDefaultTimeWhenAdding);
}
private void actualShow(Date date, Time time, boolean noDefaultTimeWhenAdding) {
dialog = new MaterialDialog.Builder(context)
.title((time == null ? date.getFormattedString() : (lesson != null ? lesson.getSubjectLongName() : date.getFormattedString())+", "+time.getStringHM()))
.customView(R.layout.dialog_event_list, true)
.neutralText(R.string.add)
.positiveText(R.string.close)
.autoDismiss(false)
.onNeutral((dialog, which) -> {
callDismissListener = false;
dialog.dismiss();
//performDismiss(dialog);
//callDismissListener = true;
new MaterialDialog.Builder(context)
.title(R.string.main_menu_add)
.items(R.array.main_menu_add_options)
.autoDismiss(true)
.itemsCallback((dialog1, itemView, position, text) -> {
callDismissListener = false;
//performDismiss(dialog1); // use if the main dialog is already dismissed
//dialog1.dismiss();
//callDismissListener = true;
switch (position) {
case 0:
new EventManualDialog(context, profileId)
.withDismissListener(this::performDismiss)
.show(app, null, date, (noDefaultTimeWhenAdding ? null : time), EventManualDialog.DIALOG_EVENT);
break;
case 1:
new EventManualDialog(context, profileId)
.withDismissListener(this::performDismiss)
.show(app, null, date, (noDefaultTimeWhenAdding ? null : time), EventManualDialog.DIALOG_HOMEWORK);
break;
}
})
.dismissListener((dialog1 -> {
callDismissListener = false;
performDismiss(dialog1);
}))
.show();
})
.onPositive((dialog, which) -> dialog.dismiss())
.dismissListener(this::performDismiss)
.show();
dialogView = dialog.getCustomView();
assert dialogView != null;
LinearLayout eventListLessonDetails = dialogView.findViewById(R.id.eventListLessonDetails);
eventListLessonDetails.setVisibility((lesson == null ? View.GONE : View.VISIBLE));
if (lesson != null) {
TextView eventListClassroom = dialogView.findViewById(R.id.eventListClassroom);
TextView eventListTeacher = dialogView.findViewById(R.id.eventListTeacher);
TextView eventListLessonChange = dialogView.findViewById(R.id.eventListLessonChange);
((TextView)dialogView.findViewById(R.id.eventListLessonDate)).setText(app.getString(R.string.date_time_format, date.getFormattedString(), ""));
boolean lessonCancelled = false;
if (lesson.changeId != 0 && lesson.changeType == LessonChange.TYPE_CANCELLED) {
lessonCancelled = true;
}
if (lessonCancelled) {
eventListLessonChange.setText(R.string.lesson_cancelled);
eventListLessonChange.setTypeface(null, Typeface.BOLD_ITALIC);
eventListTeacher.setVisibility(View.GONE);
eventListClassroom.setVisibility(View.GONE);
eventListLessonChange.setVisibility(View.VISIBLE);
} else {
eventListLessonChange.setText(lesson.getSubjectLongName(true));
if (lesson.changedSubjectLongName()) {
eventListLessonChange.setTypeface(null, Typeface.ITALIC);
} else {
eventListLessonChange.setVisibility(View.GONE);
}
eventListTeacher.setText(lesson.getTeacherFullName(true));
eventListTeacher.setTypeface(null, lesson.changedTeacherFullName() ? Typeface.ITALIC : Typeface.NORMAL);
eventListClassroom.setText(lesson.getClassroomName(true));
eventListClassroom.setTypeface(null, lesson.changedClassroomName() ? Typeface.ITALIC : Typeface.NORMAL);
}
}
//((TextView)dialogView.findViewById(R.id.textLessonDate)).setText(date.getFormattedString()+(time != null ? ", "+time.getStringHM() : ""));
examsView = dialogView.findViewById(R.id.eventListView);
examsView.setHasFixedSize(false);
examsView.setNestedScrollingEnabled(false);
examsView.setLayoutManager(new LinearLayoutManager(context));
CardView lessonChangeContainer = dialogView.findViewById(R.id.lesson_change_container);
//lessonChangeContainer.setVisibility(View.GONE);
if (time == null) {
app.db.lessonChangeDao().getLessonChangeCounterByDate(App.profileId, date).observe((LifecycleOwner) context, counter -> {
if (counter == null)
return;
if (counter.lessonChangeCount > 0) {
lessonChangeContainer.setVisibility(View.VISIBLE);
TextView lessonChangeCount = dialogView.findViewById(R.id.lesson_change_count);
lessonChangeCount.setText(Integer.toString(counter.lessonChangeCount));
lessonChangeContainer.setCardBackgroundColor(0xff78909c);
lessonChangeContainer.setOnClickListener((v -> {
new LessonChangeDialog(context).show(app, date);
}));
}
});
}
app.db.eventDao().getAllByDateTime(profileId, date, time).observe((LifecycleOwner) context, events -> {
if (events == null || events.size() == 0) {
examsView.setVisibility(View.GONE);
dialogView.findViewById(R.id.textNoEvents).setVisibility(View.VISIBLE);
}
else {
EventListAdapter adapter = new EventListAdapter(context, events, this);
examsView.setAdapter(adapter);
}
});
}
}

View File

@ -0,0 +1,886 @@
package pl.szczodrzynski.edziennik.ui.dialogs.event;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.SwitchCompat;
import androidx.lifecycle.Observer;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.google.gson.JsonObject;
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
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 com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.datamodels.Event;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.datamodels.EventType;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.datamodels.Metadata;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.datamodels.Teacher;
import pl.szczodrzynski.edziennik.datamodels.Team;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.models.Week;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.TextInputDropDown;
import pl.szczodrzynski.edziennik.utils.Themes;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_UNDEFINED;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.ns;
public class EventManualDialog {
private static final String TAG = "EventManualDialog";
private App app;
private Context context;
private int profileId;
private ProfileFull profile = null;
public EventManualDialog(Context context, int profileId) {
this.context = context;
this.activity = (AppCompatActivity) context;
this.profileId = profileId;
}
public EventManualDialog(Context context) {
this.context = context;
this.activity = (AppCompatActivity) context;
this.profileId = App.profileId;
}
private AppCompatActivity activity;
private MaterialDialog dialog;
private View dialogView;
private TextInputLayout registerEventManualDateLayout;
private TextInputDropDown registerEventManualDate;
private TextInputLayout registerEventManualLessonLayout;
private TextInputDropDown registerEventManualLesson;
private TextInputLayout registerEventManualTeamLayout;
private TextInputDropDown registerEventManualTeam;
private SwitchCompat registerEventManualShare;
private TextView registerEventManualShareText;
private TextInputLayout registerEventManualTypeLayout;
private TextInputDropDown registerEventManualType;
private TextInputLayout registerEventManualTopicLayout;
private TextInputEditText registerEventManualTopic;
private TextInputDropDown registerEventManualTeacher;
private TextInputDropDown registerEventManualSubject;
private View registerEventManualColorPreview;
private Button registerEventManualColorChoose;
private Date lessonDate = Date.getToday();
private Time lessonStartTime = null;
private long lessonTeamId = -1;
private long lessonTeacherId = -1;
private long lessonSubjectId = -1;
private boolean lessonSelected = false;
private int eventType = TYPE_UNDEFINED;
private int eventColor = -1;
private EventFull editingEvent = null;
private MaterialDialog progressDialog;
private void updateButtonCaption()
{
String buttonCaption = "";
buttonCaption += Week.getFullDayName(Week.getWeekDayFromDate(lessonDate));
buttonCaption += ", ";
buttonCaption += lessonDate.getFormattedString();
registerEventManualDate.setText(buttonCaption);
registerEventManualLesson.setText(R.string.dialog_event_manual_choose_lesson);
lessonStartTime = null;
//lessonSubjectId = -1;
//lessonTeacherId = -1;
}
private void updateSubjectTeacher() {
AsyncTask.execute(() -> {
Subject subject = app.db.subjectDao().getByIdNow(profileId, lessonSubjectId);
Teacher teacher = app.db.teacherDao().getByIdNow(profileId, lessonTeacherId);
activity.runOnUiThread(() -> {
if (lessonTeacherId == -1 || teacher == null)
registerEventManualTeacher.setText(R.string.dialog_event_manual_no_teacher);
else
registerEventManualTeacher.setText(teacher.getFullName());
if (lessonSubjectId == -1 || subject == null)
registerEventManualSubject.setText(R.string.dialog_event_manual_no_subject);
else
registerEventManualSubject.setText(subject.longName);
});
});
}
private void addEvent()
{
registerEventManualDateLayout.setError(null);
registerEventManualLessonLayout.setError(null);
registerEventManualTeamLayout.setError(null);
registerEventManualTopicLayout.setError(null);
registerEventManualTypeLayout.setError(null);
boolean errors = false;
if (!lessonSelected) {
registerEventManualLessonLayout.setError(app.getString(R.string.dialog_event_manual_lesson_choose));
errors = true;
}
if (registerEventManualShare.isChecked() && lessonTeamId == -1) {
registerEventManualTeamLayout.setError(app.getString(R.string.dialog_event_manual_team_choose));
errors = true;
}
if (eventType == -2) {
registerEventManualTypeLayout.setError(app.getString(R.string.dialog_event_manual_type_choose));
errors = true;
}
if (registerEventManualTopic.getText() == null || registerEventManualTopic.getText().toString().isEmpty()) {
registerEventManualTopicLayout.setError(app.getString(R.string.dialog_event_manual_topic_choose));
errors = true;
}
if (errors)
return;
long id = System.currentTimeMillis();
Event event = new Event(
profileId,
id,
lessonDate,
lessonStartTime,
registerEventManualTopic.getText().toString(),
eventColor,
eventType,
true,
lessonTeacherId,
lessonSubjectId,
lessonTeamId
);
Metadata metadata = new Metadata(
profileId,
eventType == TYPE_HOMEWORK ? Metadata.TYPE_HOMEWORK : Metadata.TYPE_EVENT,
event.id,
true,
true,
System.currentTimeMillis()
);
if (editingEvent != null) {
event.id = editingEvent.id;
metadata.thingId = event.id;
metadata.addedDate = editingEvent.addedDate;
}
if (registerEventManualShare.isChecked()) {
if (profile.getRegistration() != REGISTRATION_ENABLED) {
new MaterialDialog.Builder(context)
.title(R.string.dialog_event_manual_must_register_title)
.content(R.string.dialog_event_manual_must_register_text)
.positiveText(R.string.i_agree)
.negativeText(R.string.no_thanks)
.neutralText(R.string.more)
.onPositive(((dialog1, which) -> {
profile.setRegistration(REGISTRATION_ENABLED);
app.profileSaveAsync(profile);
}))
.onNeutral(((dialog1, which) -> {
new MaterialDialog.Builder(context)
.title(R.string.help)
.content(app.getString(R.string.help_register_agreement))
.positiveText(R.string.ok)
.show();
}))
.show();
return;
}
if (!profile.getEnableSharedEvents()) {
new MaterialDialog.Builder(context)
.title(R.string.dialog_event_manual_shared_disabled_title)
.content(R.string.dialog_event_manual_shared_disabled_text)
.positiveText(R.string.sure)
.negativeText(R.string.no_thanks)
.onPositive(((dialog1, which) -> {
profile.setEnableSharedEvents(true);
app.profileSaveAsync(profile);
}))
.show();
return;
}
if (editingEvent != null
&& editingEvent.sharedBy != null
&& !editingEvent.sharedBy.equals("self")) {
// editing a shared event, and event was shared by someone else
// send a edit request
shareEvent(event, metadata, MODE_REQUEST);
}
else {
// editing a self-shared event, not-shared event or a new event, but want to share it
// post a new event to database
shareEvent(event, metadata, MODE_SHARE);
}
}
else if (editingEvent != null
&& editingEvent.sharedBy != null
&& editingEvent.sharedBy.equals("self")) {
// the event was self-shared, but needs to be removed now (from the server)
shareEvent(event, metadata, MODE_UNSHARE);
}
else {
// editing a local event
finishAdding(event, metadata);
}
}
private static final int MODE_SHARE = 0;
private static final int MODE_UNSHARE = 1;
private static final int MODE_REQUEST = 2;
private static final int MODE_UNSHARE_REMOVE = 3;
private void shareEvent(Event event, Metadata metadata, int mode) {
AsyncTask.execute(() -> {
ServerRequest syncRequest =
new ServerRequest(
app,
app.requestScheme + APP_URL + "main.php?share",
"RegisterEventManualDialog/SH",
profile);
String teamUnshare = "";
if (mode == MODE_SHARE) {
if (editingEvent != null
&& event.teamId != editingEvent.teamId) {
// team ID was changed. Need to remove the event for other teams
teamUnshare = app.db.teamDao().getCodeByIdNow(profile.getId(), editingEvent.teamId);
syncRequest.setBodyParameter("team_unshare", teamUnshare);
}
syncRequest.setBodyParameter("event", app.gson.toJson(event));
syncRequest.setBodyParameter("event_addedDate", Long.toString(metadata.addedDate));
syncRequest.setBodyParameter("team", app.db.teamDao().getCodeByIdNow(profile.getId(), event.teamId));
}
else if (mode == MODE_UNSHARE
&& editingEvent != null) {
teamUnshare = app.db.teamDao().getCodeByIdNow(profile.getId(), editingEvent.teamId);
syncRequest.setBodyParameter("remove_id", Long.toString(editingEvent.id));
syncRequest.setBodyParameter("team_unshare", teamUnshare);
}
else if (mode == MODE_UNSHARE_REMOVE
&& editingEvent != null) {
teamUnshare = app.db.teamDao().getCodeByIdNow(profile.getId(), editingEvent.teamId);
syncRequest.setBodyParameter("remove_id", Long.toString(editingEvent.id));
syncRequest.setBodyParameter("team_unshare", teamUnshare);
}
else if (mode == MODE_REQUEST) {
activity.runOnUiThread(() -> {
new MaterialDialog.Builder(context)
.title(R.string.error_occured)
.content(R.string.error_shared_edit_requests_not_implemented)
.positiveText(R.string.ok)
.show();
});
return;
}
activity.runOnUiThread(() -> {
progressDialog = new MaterialDialog.Builder(context)
.title(R.string.loading)
.content(R.string.sharing_event)
.progress(true, 0)
.canceledOnTouchOutside(false)
.show();
});
String finalTeamUnshare = teamUnshare;
try {
JsonObject result = syncRequest.runSync();
activity.runOnUiThread(() -> progressDialog.dismiss());
if (result == null) {
activity.runOnUiThread(() -> new MaterialDialog.Builder(context)
.title(R.string.error_occured)
.content(R.string.sync_error_no_internet)
.positiveText(R.string.ok)
.show());
return;
}
if (result.get("success") == null || !result.get("success").getAsString().equals("true")) {
activity.runOnUiThread(() -> new MaterialDialog.Builder(context)
.title(R.string.error_occured)
.content(R.string.sync_error_app_server)
.positiveText(R.string.ok)
.show());
return;
}
if (mode == MODE_SHARE) {
event.sharedBy = "self";
event.sharedByName = profile.getStudentNameLong();
}
else if (mode == MODE_UNSHARE) {
event.sharedBy = null;
}
// check all profiles
// remove if necessary (team matching)
// this will hardly ever need to run
// (only if the user un-shares an event and has two profiles sharing the same team in common)
if (finalTeamUnshare != null && !finalTeamUnshare.equals("")) {
app.db.eventDao().removeByTeamId(editingEvent.teamId, editingEvent.id);
}
if (mode == MODE_UNSHARE_REMOVE
&& editingEvent != null) {
editingEvent.sharedBy = null;
AsyncTask.execute(() -> {
app.db.eventDao().remove(editingEvent);
});
activity.runOnUiThread(() -> {
Toast.makeText(app, R.string.removed, Toast.LENGTH_SHORT).show();
if (activity instanceof MainActivity)
((MainActivity) activity).reloadTarget();
dialog.dismiss();
});
}
else {
activity.runOnUiThread(() -> {
finishAdding(event, metadata);
});
}
} catch (Exception e) {
activity.runOnUiThread(() -> {
app.apiEdziennik.guiShowErrorDialog(activity, new AppError(TAG, 379, CODE_OTHER, null, e), R.string.error_occured);
});
}
});
}
private void finishAdding(Event event, Metadata metadata) {
AsyncTask.execute(() -> {
app.db.eventDao().add(event);
app.db.metadataDao().add(metadata);
if (editingEvent != null && (event.type == TYPE_HOMEWORK) != (editingEvent.type == TYPE_HOMEWORK)) {
// editingEvent's type changed
// the old metadata will not work because either the event changed *to* or *from* homework.
app.db.metadataDao().delete(
profileId,
editingEvent.type == TYPE_HOMEWORK ? Metadata.TYPE_HOMEWORK : Metadata.TYPE_EVENT,
editingEvent.id
);
}
});
Toast.makeText(app, R.string.saved, Toast.LENGTH_SHORT).show();
dialog.dismiss();
if (activity instanceof MainActivity)
((MainActivity) activity).reloadTarget();
}
private DialogInterface.OnDismissListener dismissListener;
private boolean callDismissListener = true;
public EventManualDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
return this;
}
private void performDismiss(DialogInterface dialogInterface) {
if (!dialog.isCancelled())
dialog.dismiss();
if (callDismissListener && dismissListener != null) {
dismissListener.onDismiss(dialogInterface);
}
callDismissListener = true;
}
public static final int DIALOG_EVENT = 0;
public static final int DIALOG_HOMEWORK = 1;
public void show(App _app, EventFull editingEvent, Date defaultDate, Time defaultTime, int dialogType) {
if (!(context instanceof Activity))
return;
this.app = _app;
AsyncTask.execute(() -> {
this.profile = app.db.profileDao().getByIdNow(profileId);
if (profile != null) {
((Activity) context).runOnUiThread(() -> {
actualShow(editingEvent, defaultDate, defaultTime, dialogType);
});
}
else {
activity.runOnUiThread(() -> {
new MaterialDialog.Builder(activity)
.title(R.string.error_occured)
.content(R.string.sync_error_profile_not_found)
.positiveText(R.string.ok)
.show();
});
}
});
}
private void actualShow(EventFull editingEvent, Date defaultDate, Time defaultTime, int dialogType) {
if (dialogType == DIALOG_HOMEWORK) {
eventType = TYPE_HOMEWORK;
eventColor = -1;
}
dialog = new MaterialDialog.Builder(context)
.title((dialogType == DIALOG_EVENT ? R.string.dialog_register_event_manual_title : R.string.dialog_register_homework_manual_title))
.customView(R.layout.dialog_event_manual, true)
.positiveText(R.string.save)
.negativeText(R.string.cancel)
.autoDismiss(false)
.onPositive((dialog, which) -> addEvent())
.onNegative((dialog, which) -> dialog.dismiss())
.onNeutral((dialog, which) -> {
if (editingEvent != null) {
String sharedNotice = "";
// notify the user that the event will be removed from all users
if (editingEvent.sharedBy != null
&& editingEvent.sharedBy.equals("self")) {
sharedNotice += "\n\n"+context.getString(R.string.dialog_event_manual_remove_shared_self);
}
// notify the user that the event will be blacklisted and removed only locally
else if (editingEvent.sharedBy != null) {
sharedNotice += "\n\n"+context.getString(R.string.dialog_event_manual_remove_shared);
}
new MaterialDialog.Builder(context)
.title(R.string.are_you_sure)
.content(context.getString(R.string.dialog_register_event_manual_remove_confirmation)+sharedNotice)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive((dialog1, which1) -> {
if (editingEvent.sharedBy != null
&& editingEvent.sharedBy.equals("self")) {
// editing event was self-shared
// the shared event needs to be unshared, then removed locally
shareEvent(editingEvent, null, MODE_UNSHARE_REMOVE);
return;
}
else if (editingEvent.sharedBy != null) {
// the event is shared by someone else
// blacklist it and remove only locally
AsyncTask.execute(() -> {
editingEvent.blacklisted = true;
app.db.eventDao().setBlacklisted(editingEvent.profileId, editingEvent.id, true);
});
}
else {
// the event is not shared nor blacklisted - just remove it
AsyncTask.execute(() -> {
app.db.eventDao().remove(editingEvent);
});
}
dialog.dismiss();
dialog1.dismiss();
Toast.makeText(app, R.string.removed, Toast.LENGTH_SHORT).show();
if (activity instanceof MainActivity)
((MainActivity) activity).reloadTarget();
})
.show();
}
})
.dismissListener((this::performDismiss))
.show();
if (editingEvent != null) {
//&& (editingEvent.sharedBy == null || editingEvent.sharedBy.equals("self"))) {
dialog.setActionButton(DialogAction.NEUTRAL, R.string.remove);
}
dialogView = dialog.getCustomView();
assert dialogView != null;
@ColorInt int primaryTextColor = Themes.INSTANCE.getPrimaryTextColor(context);
registerEventManualDateLayout = dialogView.findViewById(R.id.registerEventManualDateLayout);
registerEventManualDate = dialogView.findViewById(R.id.registerEventManualDate);
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
//registerEventManualDate.setCompoundDrawablePadding(Utils.dpToPx(6));
registerEventManualLessonLayout = dialogView.findViewById(R.id.registerEventManualLessonLayout);
registerEventManualLesson = dialogView.findViewById(R.id.registerEventManualLesson);
registerEventManualTeamLayout = dialogView.findViewById(R.id.registerEventManualTeamLayout);
registerEventManualTeam = dialogView.findViewById(R.id.registerEventManualTeam);
registerEventManualTeacher = dialogView.findViewById(R.id.registerEventManualTeacher);
registerEventManualSubject = dialogView.findViewById(R.id.registerEventManualSubject);
registerEventManualTypeLayout = dialogView.findViewById(R.id.registerEventManualTypeLayout);
registerEventManualType = dialogView.findViewById(R.id.registerEventManualType);
registerEventManualTopicLayout = dialogView.findViewById(R.id.registerEventManualTopicLayout);
registerEventManualTopic = dialogView.findViewById(R.id.registerEventManualTopic);
registerEventManualShare = dialogView.findViewById(R.id.registerEventManualShare);
registerEventManualShareText = dialogView.findViewById(R.id.registerEventManualShareText);
registerEventManualShare.setOnCheckedChangeListener(((buttonView, isChecked) -> {
// show notice text if share is enabled or event is already shared
registerEventManualShareText.setVisibility(isChecked || (editingEvent != null && editingEvent.sharedBy != null) ? View.VISIBLE : View.GONE);
// notify the user if he wants to share the event with no team selected
if (isChecked && lessonTeamId == -1) {
Toast.makeText(context, R.string.dialog_event_manual_cannot_share, Toast.LENGTH_SHORT).show();
}
if (editingEvent != null && editingEvent.sharedBy != null) {
// if the event is already shared, show the (change) or (removal) notice text
if (isChecked)
registerEventManualShareText.setText(R.string.dialog_event_manual_share_will_change);
else
registerEventManualShareText.setText(R.string.dialog_event_manual_share_will_remove);
}
}));
registerEventManualDate.setOnClickListener(v -> DatePickerDialog.newInstance(
(view, year, monthOfYear, dayOfMonth) -> {
lessonDate.year = year;
lessonDate.month = monthOfYear + 1;
lessonDate.day = dayOfMonth;
updateButtonCaption();
},
lessonDate.year,
lessonDate.month - 1,
lessonDate.day
).show(((AppCompatActivity) context).getSupportFragmentManager(), "DatePickerDialog"));
registerEventManualLesson.setOnClickListener(v -> {
app.db.lessonDao().getAllByDateWithoutChanges(profileId, lessonDate).observe(activity, new Observer<List<LessonFull>>() {
@Override
public void onChanged(@Nullable List<LessonFull> lessons) {
PopupMenu popup = new PopupMenu(context, registerEventManualLesson);
if (lessons != null && lessons.size() != 0) {
for (LessonFull lesson: lessons) {
int index = lessons.indexOf(lesson);
popup.getMenu().add(
0,
index,
index+1,
lesson.startTime.getStringHM()+" "+bs(lesson.subjectLongName));
}
}
popup.getMenu().add(1, -1, 0, R.string.dialog_event_manual_all_day);
popup.getMenu().add(1, 0, 100, R.string.dialog_event_manual_custom_time);
popup.setOnMenuItemClickListener(item -> {
if (lessons != null && item.getGroupId() == 0) {
LessonFull lesson = lessons.get(item.getItemId());
lessonTeacherId = lesson.teacherId;
lessonStartTime = lesson.startTime;
lessonSubjectId = lesson.subjectId;
registerEventManualTeacher.setText(bs(lesson.teacherFullName));
registerEventManualSubject.setText(bs(lesson.subjectLongName));
lessonTeamId = lesson.teamId;
registerEventManualTeam.setText(ns(context.getString(R.string.dialog_event_manual_no_team), bs(lesson.teamName)));
registerEventManualLesson.setText(item.getTitle());
lessonSelected = true;
}
else if (item.getGroupId() == 1) {
if (item.getItemId() == -1) {
lessonStartTime = null;
lessonTeacherId = -1;
lessonSubjectId = -1;
if (editingEvent != null) {
lessonTeacherId = editingEvent.teacherId;
lessonSubjectId = editingEvent.subjectId;
}
updateSubjectTeacher();
registerEventManualLesson.setText(item.getTitle());
lessonSelected = true;
return true;
}
Time now = Time.getNow();
TimePickerDialog.newInstance((view, hourOfDay, minute, second) -> {
lessonStartTime = new Time(hourOfDay, minute, second);
lessonTeacherId = -1;
lessonSubjectId = -1;
if (editingEvent != null) {
lessonTeacherId = editingEvent.teacherId;
lessonSubjectId = editingEvent.subjectId;
}
updateSubjectTeacher();
//lessonTeamId = -1;
registerEventManualLesson.setText(lessonStartTime.getStringHM());
lessonSelected = true;
}, now.hour, now.minute, 0, true).show(((AppCompatActivity) context).getSupportFragmentManager(), "TimePickerDialog");
}
return true;
});
popup.show();
app.db.lessonDao().getAllByDateWithoutChanges(profileId, lessonDate).removeObserver(this);
}
});
});
registerEventManualTeam.setOnClickListener(v -> {
app.db.teamDao().getAll(profileId).observe(activity, new Observer<List<Team>>() {
@Override
public void onChanged(@Nullable List<Team> teams) {
PopupMenu popup = new PopupMenu(context, registerEventManualTeam);
if (teams != null && teams.size() != 0) {
int index = 0;
for (Team team : teams) {
popup.getMenu().add(0, index++, (index)+1, team.name);
}
}
popup.getMenu().add(1, 0, 100, context.getString(R.string.dialog_event_manual_no_team));
popup.setOnMenuItemClickListener(item -> {
long id = -1;
if (item.getGroupId() == 0 && teams != null) {
Team team = teams.get(item.getItemId());
id = team.id;
}
lessonTeamId = id;
registerEventManualTeam.setText(item.getTitle());
return true;
});
popup.show();
app.db.teamDao().getAll(profileId).removeObserver(this);
}
});
});
registerEventManualTeacher.setOnClickListener(v -> {
app.db.teacherDao().getAllTeachers(profileId).observe(activity, new Observer<List<Teacher>>() {
@Override
public void onChanged(@Nullable List<Teacher> teachers) {
PopupMenu popup = new PopupMenu(context, registerEventManualTeacher);
if (teachers != null && teachers.size() != 0) {
int index = 0;
for (Teacher teacher : teachers) {
popup.getMenu().add(0, index++, (index)+1, teacher.getFullName());
}
}
popup.getMenu().add(1, 0, 0, context.getString(R.string.dialog_event_manual_no_teacher));
popup.setOnMenuItemClickListener(item -> {
long id = -1;
if (item.getGroupId() == 0 && teachers != null) {
Teacher teacher = teachers.get(item.getItemId());
id = teacher.id;
}
lessonTeacherId = id;
registerEventManualTeacher.setText(item.getTitle());
return true;
});
popup.show();
app.db.teacherDao().getAllTeachers(profileId).removeObserver(this);
}
});
});
registerEventManualSubject.setOnClickListener(v -> {
app.db.subjectDao().getAll(profileId).observe(activity, new Observer<List<Subject>>() {
@Override
public void onChanged(@Nullable List<Subject> subjects) {
PopupMenu popup = new PopupMenu(context, registerEventManualSubject);
if (subjects != null && subjects.size() != 0) {
int index = 0;
for (Subject subject: subjects) {
popup.getMenu().add(0, index++, (index)+1, subject.longName);
}
}
popup.getMenu().add(1, 0, 0, context.getString(R.string.dialog_event_manual_no_subject));
popup.setOnMenuItemClickListener(item -> {
long id = -1;
if (item.getGroupId() == 0 && subjects != null) {
Subject subject = subjects.get(item.getItemId());
id = subject.id;
}
lessonSubjectId = id;
registerEventManualSubject.setText(item.getTitle());
return true;
});
popup.show();
app.db.subjectDao().getAll(profileId).removeObserver(this);
}
});
});
registerEventManualType.setOnClickListener(v -> {
app.db.eventTypeDao().getAll(profileId).observe(activity, new Observer<List<EventType>>() {
@Override
public void onChanged(@Nullable List<EventType> eventTypes) {
PopupMenu popup = new PopupMenu(context, registerEventManualType);
if (eventTypes != null && eventTypes.size() != 0) {
int index = 0;
for (EventType eventType : eventTypes) {
popup.getMenu().add(0, index++, (index)+1, eventType.name);
}
}
popup.setOnMenuItemClickListener(item -> {
if (item.getGroupId() == 0 && eventTypes != null) {
EventType typeObj = eventTypes.get(item.getItemId());
eventType = typeObj.id;
eventColor = -1; // set -1 as it's the event type's default color
registerEventManualColorPreview.setBackgroundColor(typeObj.color); // set event type's color here to show how will it look
}
registerEventManualType.setText(item.getTitle());
return true;
});
popup.show();
app.db.eventTypeDao().getAll(profileId).removeObserver(this);
}
});
});
View.OnClickListener colorChooseOnClick = (v -> {
AsyncTask.execute(() -> {
EventType type = app.db.eventTypeDao().getByIdNow(profileId, eventType);
activity.runOnUiThread(() -> {
ColorPickerDialog colorPickerDialog = ColorPickerDialog.newBuilder()
.setColor(eventColor != -1 ? eventColor : type != null ? type.color : COLOR_DEFAULT)
.create();
colorPickerDialog.setColorPickerDialogListener(
new ColorPickerDialogListener() {
@Override
public void onColorSelected(int dialogId, int color) {
registerEventManualColorPreview.setBackgroundColor(color);
eventColor = color;
}
@Override
public void onDialogDismissed(int dialogId) {
}
});
colorPickerDialog.show(((Activity)context).getFragmentManager(), "color-picker-dialog");
});
});
});
registerEventManualColorPreview = dialogView.findViewById(R.id.registerEventManualColorPreview);
registerEventManualColorPreview.setOnClickListener(colorChooseOnClick);
registerEventManualColorChoose = dialogView.findViewById(R.id.registerEventManualColorChoose);
registerEventManualColorChoose.setOnClickListener(colorChooseOnClick);
if (eventType != TYPE_UNDEFINED) {
AsyncTask.execute(() -> {
EventType type = app.db.eventTypeDao().getByIdNow(profileId, eventType);
activity.runOnUiThread(() -> {
if (type != null) {
eventColor = -1;
registerEventManualColorPreview.setBackgroundColor(type.color);
registerEventManualType.setText(type.name);
}
else {
eventColor = -1;
eventType = TYPE_UNDEFINED;
registerEventManualColorPreview.setBackgroundColor(COLOR_DEFAULT);
registerEventManualType.setText("");
}
});
});
}
else {
registerEventManualColorPreview.setBackgroundColor(COLOR_DEFAULT);
eventColor = -1;
}
/*if (dialogType == DIALOG_HOMEWORK) {
dialogView.findViewById(R.id.registerEventManualColorContainer).setVisibility(View.GONE);
registerEventManualType.setVisibility(View.GONE);
eventType = TYPE_HOMEWORK;
}
else {
}*/
updateButtonCaption();
if (editingEvent != null) {
this.editingEvent = editingEvent;
lessonDate = editingEvent.eventDate.clone();
lessonTeamId = editingEvent.teamId;
// check the (shared) checkbox if the event is already shared by someone
registerEventManualShare.setChecked(editingEvent.sharedBy != null);
// .. but disable the ability to un-share, if it's shared by someone else
registerEventManualShare.setEnabled(editingEvent.sharedBy == null || editingEvent.sharedBy.equals("self"));
registerEventManualShare.jumpDrawablesToCurrentState();
// show the notice text if the event is already shared
registerEventManualShareText.setVisibility(editingEvent.sharedBy != null ? View.VISIBLE : View.GONE);
if (editingEvent.sharedBy != null) {
// show the (change) notice if the event is shared by (self)
if (editingEvent.sharedBy.equals("self"))
registerEventManualShareText.setText(R.string.dialog_event_manual_share_will_change);
// otherwise show the (edit request) notice
else
registerEventManualShareText.setText(app.getString(R.string.dialog_event_manual_share_will_request, editingEvent.sharedByName));
}
updateButtonCaption(); // updates the lesson date and clears the time
lessonSubjectId = editingEvent.subjectId;
lessonTeacherId = editingEvent.teacherId;
lessonStartTime = editingEvent.startTime == null ? null : editingEvent.startTime.clone();
lessonSelected = true;
registerEventManualTeam.setText(ns(context.getString(R.string.dialog_event_manual_no_team), editingEvent.teamName));
registerEventManualTopic.setText(editingEvent.topic);
registerEventManualType.setText(editingEvent.typeName);
eventColor = editingEvent.getColor();
eventType = editingEvent.type;
registerEventManualColorPreview.setBackgroundColor(eventColor);
if (lessonSubjectId == -1) {
//lessonTeacherId = -1; // why tho?
registerEventManualLesson.setText(lessonStartTime == null ? app.getString(R.string.dialog_event_manual_all_day) : lessonStartTime.getStringHM());
}
else {
registerEventManualLesson.setText((lessonStartTime == null ? app.getString(R.string.dialog_event_manual_all_day) + "," : lessonStartTime.getStringHM())+" "+bs(editingEvent.subjectLongName));
}
}
else {
if (defaultDate != null) {
lessonDate = defaultDate.clone();
updateButtonCaption();
}
if (defaultDate != null && defaultTime != null) { // ONLY used when adding an event to the previously selected lesson
AsyncTask.execute(() -> {
LessonFull lesson = app.db.lessonDao().getByDateTimeWithoutChangesNow(profileId, defaultDate, defaultTime);
lessonTeacherId = lesson.teacherId;
lessonStartTime = lesson.startTime.clone();
lessonTeamId = lesson.teamId;
lessonSubjectId = lesson.subjectId;
lessonSelected = true;
activity.runOnUiThread(() -> {
registerEventManualTeam.setText(ns(context.getString(R.string.dialog_event_manual_no_team), lesson.teamName));
registerEventManualLesson.setText(lesson.startTime.getStringHM()+" "+bs(lesson.subjectLongName));
registerEventManualTeacher.setText(ns(context.getString(R.string.dialog_event_manual_no_teacher), lesson.teacherFullName));
registerEventManualSubject.setText(ns(context.getString(R.string.dialog_event_manual_no_subject), lesson.subjectLongName));
});
});
}
else {
AsyncTask.execute(() -> {
Team team = app.db.teamDao().getClassNow(profileId);
if (team != null) {
activity.runOnUiThread(() -> {
lessonTeamId = team.id;
registerEventManualTeam.setText(ns(context.getString(R.string.dialog_event_manual_no_team), team.name));
});
}
});
}
}
updateSubjectTeacher();
}
}

View File

@ -0,0 +1,129 @@
package pl.szczodrzynski.edziennik.ui.dialogs.grade;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import android.view.View;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListAdapter;
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding;
import pl.szczodrzynski.edziennik.datamodels.Grade;
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
import pl.szczodrzynski.edziennik.utils.Colors;
import static pl.szczodrzynski.edziennik.datamodels.Profile.COLOR_MODE_DEFAULT;
public class GradeDetailsDialog {
private App app;
private Context context;
private int profileId;
public GradeDetailsDialog(Context context) {
this.context = context;
this.profileId = App.profileId;
}
public GradeDetailsDialog(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
public MaterialDialog dialog;
private DialogGradeDetailsBinding b;
private DialogInterface.OnDismissListener dismissListener;
public boolean callDismissListener = true;
public GradeDetailsDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
return this;
}
public void performDismiss(DialogInterface dialogInterface) {
if (callDismissListener && dismissListener != null) {
dismissListener.onDismiss(dialogInterface);
}
callDismissListener = true;
}
public void show(App _app, GradeFull grade)
{
this.app = _app;
dialog = new MaterialDialog.Builder(context)
.customView(R.layout.dialog_grade_details, true)
.positiveText(R.string.close)
.autoDismiss(false)
.onPositive((dialog, which) -> dialog.dismiss())
.dismissListener(this::performDismiss)
.show();
View root = dialog.getCustomView();
assert root != null;
b = DialogGradeDetailsBinding.bind(root);
b.setGrade(grade);
int gradeColor;
if (app.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {
gradeColor = Colors.gradeToColor(grade);
}
DecimalFormat format = new DecimalFormat("#.##");
if (grade.weight < 0) {
grade.weight *= -1;
}
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_BEHAVIOUR) {
b.setWeightText(null);
grade.weight = 0;
}
else {
if (grade.type == Grade.TYPE_POINT) {
b.setWeightText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
}
else if (grade.weight == 0) {
b.setWeightText(app.getString(R.string.grades_weight_not_counted));
}
else {
b.setWeightText(app.getString(R.string.grades_weight_format, format.format(grade.weight)));
}
}
b.setCommentVisible(false);
b.setDevMode(App.devMode);
b.gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
b.gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
AsyncTask.execute(() -> {
List<GradeFull> historyList = app.db.gradeDao().getAllWithParentIdNow(profileId, grade.id);
if (historyList.size() == 0) {
b.setHistoryVisible(false);
return;
}
b.setHistoryVisible(true);
b.gradeHistoryNest.post(() -> {
b.gradeHistoryNest.setNestedScrollingEnabled(false);
b.gradeHistoryList.setHasFixedSize(false);
b.gradeHistoryList.setNestedScrollingEnabled(false);
b.gradeHistoryList.setLayoutManager(new LinearLayoutManager(context));
b.gradeHistoryList.setAdapter(new GradesListAdapter(context, historyList));
});
});
}
}

View File

@ -0,0 +1,78 @@
package pl.szczodrzynski.edziennik.ui.dialogs.lessonchange;
import android.content.Context;
import android.view.View;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableAdapter;
import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
public class LessonChangeDialog {
private App app;
private Context context;
private int profileId;
public LessonChangeDialog(Context context) {
this.context = context;
this.profileId = App.profileId;
}
public LessonChangeDialog(Context context, int profileId) {
this.context = context;
this.profileId = profileId;
}
private MaterialDialog dialog;
private DialogLessonChangeListBinding b;
public void show(App _app, Date date)
{
this.app = _app;
dialog = new MaterialDialog.Builder(context)
.title(date.getFormattedString())
.customView(R.layout.dialog_lesson_change_list, true)
.positiveText(R.string.close)
.autoDismiss(false)
.onPositive((dialog, which) -> dialog.dismiss())
.show();
if (dialog.getCustomView() == null)
return;
b = DataBindingUtil.bind(dialog.getCustomView());
if (b == null)
return;
b.lessonChangeView.setHasFixedSize(false);
b.lessonChangeView.setNestedScrollingEnabled(false);
b.lessonChangeView.setLayoutManager(new LinearLayoutManager(context));
app.db.lessonDao().getAllByDate(profileId, date, Time.getNow()).observe((LifecycleOwner) context, lessons -> {
if (app == null || app.profile == null || b == null)
return;
List<LessonFull> changedLessons = new ArrayList<>();
for (LessonFull lesson: lessons) {
if (lesson.changeId != 0) {
changedLessons.add(lesson);
}
}
app.db.eventDao().getAllByDate(profileId, date).observe((LifecycleOwner) context, events -> {
TimetableAdapter adapter = new TimetableAdapter(context, date, changedLessons, events == null ? new ArrayList<>() : events);
b.lessonChangeView.setAdapter(adapter);
b.lessonChangeView.setVisibility(View.VISIBLE);
});
});
}
}

View File

@ -0,0 +1,243 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda;
import com.github.tibolte.agendacalendarview.models.CalendarEvent;
import com.github.tibolte.agendacalendarview.models.IDayItem;
import com.github.tibolte.agendacalendarview.models.IWeekItem;
import java.util.Calendar;
import pl.szczodrzynski.edziennik.utils.models.Date;
public class LessonChangeEvent implements CalendarEvent {
/**
* Id of the event.
*/
private long mId;
/**
* Color to be displayed in the agenda view.
*/
private int mColor;
/**
* Text color displayed on the background color
*/
private int mTextColor;
/**
* Calendar instance helping sorting the events per section in the agenda view.
*/
private Calendar mInstanceDay;
/**
* Start time of the event.
*/
private Calendar mStartTime;
/**
* End time of the event.
*/
private Calendar mEndTime;
/**
* References to a DayItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private IDayItem mDayReference;
/**
* References to a WeekItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private IWeekItem mWeekReference;
private int profileId;
private Date lessonChangeDate;
private int lessonChangeCount;
public LessonChangeEvent(LessonChangeEvent calendarEvent) {
this.mId = calendarEvent.getId();
this.mColor = calendarEvent.getColor();
this.mTextColor = calendarEvent.getTextColor();
this.mStartTime = calendarEvent.getStartTime();
this.mEndTime = calendarEvent.getEndTime();
this.profileId = calendarEvent.getProfileId();
this.lessonChangeDate = calendarEvent.getLessonChangeDate();
this.lessonChangeCount = calendarEvent.getLessonChangeCount();
}
public LessonChangeEvent(long mId, int mColor, int mTextColor, Calendar mStartTime, Calendar mEndTime, int profileId, Date lessonChangeDate, int lessonChangeCount) {
this.mId = mId;
this.mColor = mColor;
this.mTextColor = mTextColor;
this.mStartTime = mStartTime;
this.mEndTime = mEndTime;
this.profileId = profileId;
this.lessonChangeDate = lessonChangeDate;
this.lessonChangeCount = lessonChangeCount;
}
public int getProfileId() {
return profileId;
}
public Date getLessonChangeDate() {
return lessonChangeDate;
}
public int getLessonChangeCount() {
return lessonChangeCount;
}
public void setProfileId(int profileId) {
this.profileId = profileId;
}
public void setLessonChangeDate(Date lessonChangeDate) {
this.lessonChangeDate = lessonChangeDate;
}
public void setLessonChangeCount(int lessonChangeCount) {
this.lessonChangeCount = lessonChangeCount;
}
@Override
public void setPlaceholder(boolean placeholder) {
}
@Override
public boolean isPlaceholder() {
return false;
}
@Override
public String getLocation() {
return null;
}
@Override
public void setLocation(String mLocation) {
}
@Override
public long getId() {
return mId;
}
@Override
public void setId(long mId) {
this.mId = mId;
}
@Override
public boolean getShowBadge() {
return false;
}
@Override
public void setShowBadge(boolean mShowBadge) {
}
@Override
public int getTextColor() {
return mTextColor;
}
@Override
public void setTextColor(int mTextColor) {
this.mTextColor = mTextColor;
}
@Override
public String getDescription() {
return null;
}
@Override
public void setDescription(String mDescription) {
}
@Override
public boolean isAllDay() {
return false;
}
@Override
public void setAllDay(boolean allDay) {
}
@Override
public Calendar getStartTime() {
return mStartTime;
}
@Override
public void setStartTime(Calendar mStartTime) {
this.mStartTime = mStartTime;
}
@Override
public Calendar getEndTime() {
return mEndTime;
}
@Override
public void setEndTime(Calendar mEndTime) {
this.mEndTime = mEndTime;
}
@Override
public String getTitle() {
return null;
}
@Override
public void setTitle(String mTitle) {
}
@Override
public Calendar getInstanceDay() {
return mInstanceDay;
}
@Override
public void setInstanceDay(Calendar mInstanceDay) {
this.mInstanceDay = mInstanceDay;
this.mInstanceDay.set(Calendar.HOUR, 0);
this.mInstanceDay.set(Calendar.MINUTE, 0);
this.mInstanceDay.set(Calendar.SECOND, 0);
this.mInstanceDay.set(Calendar.MILLISECOND, 0);
this.mInstanceDay.set(Calendar.AM_PM, 0);
}
@Override
public IDayItem getDayReference() {
return mDayReference;
}
@Override
public void setDayReference(IDayItem mDayReference) {
this.mDayReference = mDayReference;
}
@Override
public IWeekItem getWeekReference() {
return mWeekReference;
}
@Override
public void setWeekReference(IWeekItem mWeekReference) {
this.mWeekReference = mWeekReference;
}
@Override
public CalendarEvent copy() {
return new LessonChangeEvent(this);
}
@Override
public int getColor() {
return mColor;
}
}

View File

@ -0,0 +1,28 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda;
import android.view.View;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import com.github.tibolte.agendacalendarview.render.EventRenderer;
import pl.szczodrzynski.edziennik.R;
public class LessonChangeEventRenderer extends EventRenderer<LessonChangeEvent> {
@Override
public void render(View view, LessonChangeEvent event) {
CardView card = view.findViewById(R.id.lesson_change_card);
TextView changeText = view.findViewById(R.id.lesson_change_text);
TextView changeCount = view.findViewById(R.id.lesson_change_count);
card.setCardBackgroundColor(event.getColor());
changeText.setTextColor(event.getTextColor());
changeCount.setTextColor(event.getTextColor());
changeCount.setText(String.valueOf(event.getLessonChangeCount()));
}
@Override
public int getEventLayout() {
return R.layout.agenda_event_lesson_change;
}
}

View File

@ -0,0 +1,138 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.applandeo.materialcalendarview.CalendarView;
import com.applandeo.materialcalendarview.EventDay;
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.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterAgendaCalendarBinding;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.Themes;
import static pl.szczodrzynski.edziennik.utils.Utils.intToStr;
public class RegisterAgendaCalendarFragment extends Fragment {
private App app = null;
private Activity activity = null;
private FragmentRegisterAgendaCalendarBinding b = null;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activity = 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_agenda_calendar, container, false);
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;
List<Integer> unreadEventDates = new ArrayList<>();
final Handler handler = new Handler();
handler.postDelayed(() -> AsyncTask.execute(() -> {
Context c = getContext();
Activity a = getActivity();
assert c != null;
assert a != null;
if (!isAdded()) {
return;
}
List<EventDay> eventList = new ArrayList<>();
List<EventFull> events = app.db.eventDao().getAllNow(App.profileId);
for (EventFull event : events) {
if (event.eventDate == null)
continue;
Calendar startTime = Calendar.getInstance();
startTime.set(
event.eventDate.year,
event.eventDate.month - 1,
event.eventDate.day,
event.startTime == null ? 0 : event.startTime.hour,
event.startTime == null ? 0 : event.startTime.minute,
event.startTime == null ? 0 : event.startTime.second
);
Drawable eventIcon = new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_checkbox_blank_circle).size(IconicsSize.dp(10)).color(IconicsColor.colorInt(event.getColor()));
eventList.add(new EventDay(startTime, eventIcon));
if (!event.seen) {
unreadEventDates.add(event.eventDate.getValue());
}
}
List<LessonFull> lessonChanges = app.db.lessonChangeDao().getAllChangesWithLessonsNow(App.profileId);
for (LessonFull lesson: lessonChanges) {
Calendar startTime = Calendar.getInstance();
if (lesson.lessonDate == null) {
continue;
}
startTime.set(
lesson.lessonDate.year,
lesson.lessonDate.month - 1,
lesson.lessonDate.day,
lesson.startTime.hour,
lesson.startTime.minute,
lesson.startTime.second);
Drawable eventIcon = new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_checkbox_blank_circle).size(IconicsSize.dp(10)).color(IconicsColor.colorInt(0xff78909c));
eventList.add(new EventDay(startTime, eventIcon));
}
getActivity().runOnUiThread(() -> {
//List<EventDay> eventList = new ArrayList<>();
//Collections.sort(eventList, new EventListComparator());
CalendarView calendarView = b.agendaCalendarView;
calendarView.setEvents(eventList);
calendarView.setOnDayClickListener(eventDay -> {
Date dayDate = Date.fromCalendar(eventDay.getCalendar());
int scrolledDate = dayDate.getValue();
if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate));
}
new EventListDialog(getContext()).show(app, dayDate);
});
b.progressBar.setVisibility(View.GONE);
});
}), 300);
}
}

View File

@ -0,0 +1,365 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.applandeo.materialcalendarview.CalendarView;
import com.applandeo.materialcalendarview.EventDay;
import com.github.tibolte.agendacalendarview.AgendaCalendarView;
import com.github.tibolte.agendacalendarview.CalendarPickerController;
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent;
import com.github.tibolte.agendacalendarview.models.CalendarEvent;
import com.github.tibolte.agendacalendarview.models.IDayItem;
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.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterAgendaCalendarBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterAgendaDefaultBinding;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.models.db.LessonChangeCounter;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_EVENT;
import static pl.szczodrzynski.edziennik.datamodels.Profile.AGENDA_CALENDAR;
import static pl.szczodrzynski.edziennik.datamodels.Profile.AGENDA_DEFAULT;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.intToStr;
public class RegisterAgendaDefaultFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentRegisterAgendaDefaultBinding b_default = null;
private FragmentRegisterAgendaCalendarBinding b_calendar = null;
private int viewType = AGENDA_DEFAULT;
@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
viewType = app.profile.getAgendaViewType();
if (viewType == AGENDA_DEFAULT) {
b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_register_agenda_default, container, false);
return b_default.getRoot();
}
else {
b_calendar = DataBindingUtil.inflate(inflater, R.layout.fragment_register_agenda_calendar, container, false);
return b_calendar.getRoot();
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || (b_default == null && b_calendar == null) || !isAdded())
return;
activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
new MaterialDialog.Builder(activity)
.title(R.string.main_menu_add)
.items(R.array.main_menu_add_options)
.itemsCallback((dialog, itemView, position, text) -> {
switch (position) {
case 0:
new EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_EVENT);
break;
case 1:
new EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK);
break;
}
})
.show();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view)
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar : CommunityMaterial.Icon2.cmd_view_list)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
app.profile.setAgendaViewType(viewType);
app.profileSaveAsync();
activity.reloadTarget();
}),
new BottomSheetSeparatorItem(true),
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_EVENT, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
activity.gainAttention();
if (viewType == AGENDA_DEFAULT) {
List<Integer> unreadEventDates = new ArrayList<>();
final Handler handler = new Handler();
handler.postDelayed(() -> AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b_default == null || !isAdded())
return;
List<CalendarEvent> eventList = new ArrayList<>();
List<LessonChangeCounter> lessonChangeCounters = app.db.lessonChangeDao().getLessonChangeCountersNow(App.profileId);
for (LessonChangeCounter counter : lessonChangeCounters) {
Calendar startTime = Calendar.getInstance();
Calendar endTime = Calendar.getInstance();
if (counter.lessonChangeDate == null) {
continue;
}
startTime.set(counter.lessonChangeDate.year, counter.lessonChangeDate.month - 1, counter.lessonChangeDate.day, 10, 0, 0);
endTime.setTimeInMillis(startTime.getTimeInMillis() + (1000 * 60 * 45));
eventList.add(new LessonChangeEvent(
counter.lessonChangeDate.getInMillis(),
0xff78909c,
Colors.legibleTextColor(0xff78909c),
startTime,
endTime,
counter.profileId,
counter.lessonChangeDate,
counter.lessonChangeCount
));
}
List<EventFull> events = app.db.eventDao().getAllNow(App.profileId);
for (EventFull event : events) {
Calendar startTime = Calendar.getInstance();
Calendar endTime = Calendar.getInstance();
if (event.eventDate == null)
continue;
startTime.set(
event.eventDate.year,
event.eventDate.month - 1,
event.eventDate.day,
event.startTime == null ? 0 : event.startTime.hour,
event.startTime == null ? 0 : event.startTime.minute,
event.startTime == null ? 0 : event.startTime.second
);
endTime.setTimeInMillis(startTime.getTimeInMillis() + (1000 * 60 * 45));
eventList.add(new BaseCalendarEvent(event.typeName + " - " + event.topic,
"",
(event.startTime == null ? getString(R.string.agenda_event_all_day) : event.startTime.getStringHM()) +
Utils.bs(", ", event.subjectLongName) +
Utils.bs(", ", event.teacherFullName) +
Utils.bs(", ", event.teamName),
event.getColor(),
Colors.legibleTextColor(event.getColor()),
startTime,
endTime,
event.startTime == null,
event.id, !event.seen));
if (!event.seen) {
unreadEventDates.add(event.eventDate.getValue());
}
}
/*List<LessonFull> lessonChanges = app.db.lessonChangeDao().getAllChangesWithLessonsNow(App.profileId);
for (LessonFull lesson: lessonChanges) {
Calendar startTime = Calendar.getInstance();
Calendar endTime = Calendar.getInstance();
if (lesson.lessonDate == null) {
continue;
}
startTime.set(lesson.lessonDate.year, lesson.lessonDate.month - 1, lesson.lessonDate.day, lesson.startTime.hour, lesson.startTime.minute, lesson.startTime.second);
endTime.setTimeInMillis(startTime.getTimeInMillis() + (1000 * 60 * 45));
String description = lesson.changeTypeStr(activity);
if (lesson.changeType != TYPE_CANCELLED) {
if (lesson.subjectId != lesson.changeSubjectId && lesson.teacherId != lesson.changeTeacherId) {
description += " -> " + bs(null, lesson.changeSubjectLongName, ", ") + bs(lesson.changeTeacherFullName);
} else if (lesson.subjectId != lesson.changeSubjectId) {
description += " -> " + bs(lesson.changeSubjectLongName);
} else if (lesson.teacherId != lesson.changeTeacherId) {
description += " -> " + bs(lesson.changeTeacherFullName);
}
}
eventList.add(new BaseCalendarEvent(description,
"",
(lesson.startTime.getStringHM()) +
Utils.bs(", ", lesson.subjectLongName) +
Utils.bs(", ", lesson.teacherFullName) +
Utils.bs(", ", lesson.teamName),
0xff78909c,
Colors.legibleTextColor(0xff78909c),
startTime,
endTime,
false,
(int)lesson.changeId, false));
}*/
activity.runOnUiThread(() -> {
AgendaCalendarView mAgendaCalendarView = b_default.agendaDefaultView;
// minimum and maximum date of our calendar
// 2 month behind, one year ahead, example: March 2015 <-> May 2015 <-> May 2016
Calendar minDate = Calendar.getInstance();
Calendar maxDate = Calendar.getInstance();
minDate.add(Calendar.MONTH, -2);
minDate.set(Calendar.DAY_OF_MONTH, 1);
maxDate.add(Calendar.MONTH, 2);
mAgendaCalendarView.init(eventList, minDate, maxDate, Locale.getDefault(), new CalendarPickerController() {
@Override
public void onDaySelected(IDayItem dayItem) {
}
@Override
public void onScrollToDate(Calendar calendar) {
int scrolledDate = Date.fromCalendar(calendar).getValue();
if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate));
}
}
@Override
public void onEventSelected(CalendarEvent calendarEvent) {
if (calendarEvent instanceof BaseCalendarEvent) {
if (!calendarEvent.isPlaceholder() && !calendarEvent.isAllDay()) {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()), Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()), true);
} else {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
}
} else if (calendarEvent instanceof LessonChangeEvent) {
new LessonChangeDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
//Toast.makeText(app, "Clicked "+((LessonChangeEvent) calendarEvent).getLessonChangeDate().getFormattedString(), Toast.LENGTH_SHORT).show();
}
}
}, new LessonChangeEventRenderer());
b_default.progressBar.setVisibility(View.GONE);
});
}), 500);
}
else {
List<Integer> unreadEventDates = new ArrayList<>();
final Handler handler = new Handler();
handler.postDelayed(() -> AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b_calendar == null || !isAdded())
return;
Context c = getContext();
Activity a = getActivity();
assert c != null;
assert a != null;
if (!isAdded()) {
return;
}
List<EventDay> eventList = new ArrayList<>();
List<EventFull> events = app.db.eventDao().getAllNow(App.profileId);
for (EventFull event : events) {
if (event.eventDate == null)
continue;
Calendar startTime = Calendar.getInstance();
startTime.set(
event.eventDate.year,
event.eventDate.month - 1,
event.eventDate.day,
event.startTime == null ? 0 : event.startTime.hour,
event.startTime == null ? 0 : event.startTime.minute,
event.startTime == null ? 0 : event.startTime.second
);
Drawable eventIcon = new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_checkbox_blank_circle).size(IconicsSize.dp(10)).color(IconicsColor.colorInt(event.getColor()));
eventList.add(new EventDay(startTime, eventIcon));
if (!event.seen) {
unreadEventDates.add(event.eventDate.getValue());
}
}
List<LessonFull> lessonChanges = app.db.lessonChangeDao().getAllChangesWithLessonsNow(App.profileId);
for (LessonFull lesson: lessonChanges) {
Calendar startTime = Calendar.getInstance();
if (lesson.lessonDate == null) {
continue;
}
startTime.set(
lesson.lessonDate.year,
lesson.lessonDate.month - 1,
lesson.lessonDate.day,
lesson.startTime.hour,
lesson.startTime.minute,
lesson.startTime.second);
Drawable eventIcon = new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_checkbox_blank_circle).size(IconicsSize.dp(10)).color(IconicsColor.colorInt(0xff78909c));
eventList.add(new EventDay(startTime, eventIcon));
}
getActivity().runOnUiThread(() -> {
//List<EventDay> eventList = new ArrayList<>();
//Collections.sort(eventList, new EventListComparator());
CalendarView calendarView = b_calendar.agendaCalendarView;
calendarView.setEvents(eventList);
calendarView.setOnDayClickListener(eventDay -> {
Date dayDate = Date.fromCalendar(eventDay.getCalendar());
int scrolledDate = dayDate.getValue();
if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate));
}
new EventListDialog(getContext()).show(app, dayDate);
});
b_calendar.progressBar.setVisibility(View.GONE);
});
}), 300);
}
}
public static class EventListComparator implements java.util.Comparator<CalendarEvent> {
@Override
public int compare(CalendarEvent o1, CalendarEvent o2) {
return Long.compare(o1.getStartTime().getTimeInMillis(), o2.getStartTime().getTimeInMillis());
//return (int)(o1.getStartTime().getTimeInMillis() - o2.getStartTime().getTimeInMillis());
}
}
}

View File

@ -0,0 +1,83 @@
package pl.szczodrzynski.edziennik.ui.modules.announcements;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.RowAnnouncementsItemBinding;
import pl.szczodrzynski.edziennik.datamodels.AnnouncementFull;
public class AnnouncementsAdapter extends RecyclerView.Adapter<AnnouncementsAdapter.ViewHolder> {
private Context context;
public List<AnnouncementFull> announcementList;
public OnAnnouncementClickListener onClick;
public interface OnAnnouncementClickListener {
void onClick(View v, AnnouncementFull announcement);
}
public AnnouncementsAdapter(Context context, List<AnnouncementFull> announcementList, OnAnnouncementClickListener onClick) {
//setHasStableIds(true);
this.context = context;
this.announcementList = announcementList;
this.onClick = onClick;
}
@Override
public int getItemCount() {
return announcementList.size();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.row_announcements_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int groupPosition) {
AnnouncementFull item = announcementList.get(groupPosition);
RowAnnouncementsItemBinding b = holder.b;
b.announcementsItemCard.setOnClickListener((v -> {
if (onClick != null) {
onClick.onClick(v, item);
}
}));
b.announcementsItemSender.setText(item.teacherFullName);
b.announcementsItemTitle.setText(item.subject);
b.announcementsItemText.setText(item.text);
b.announcementsItemDate.setText(item.startDate.getFormattedString()+(item.endDate == null ? "" : " - "+item.endDate.getFormattedString()));
if (!item.seen) {
b.announcementsItemTitle.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
b.announcementsItemTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
b.announcementsItemSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
b.announcementsItemTitle.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
} else {
b.announcementsItemSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.announcementsItemTitle.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.announcementsItemTitle.setBackground(null);
}
}
class ViewHolder extends RecyclerView.ViewHolder {
RowAnnouncementsItemBinding b;
ViewHolder(RowAnnouncementsItemBinding b) {
super(b.getRoot());
this.b = b;
}
}
}

View File

@ -0,0 +1,131 @@
package pl.szczodrzynski.edziennik.ui.modules.announcements;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.DialogAnnouncementBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterSchoolNoticesBinding;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_ANNOUNCEMENT;
public class RegisterAnnouncementsFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentRegisterSchoolNoticesBinding b = 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_school_notices, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
return b.getRoot();
}
private RecyclerView recyclerView;
@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_ANNOUNCEMENT, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_ANNOUNCEMENTS, b.refreshLayout);
});*/
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
//RecyclerViewExpandableItemManager expMgr = new RecyclerViewExpandableItemManager(null);
recyclerView = b.announcementsView;
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(linearLayoutManager);
app.db.announcementDao().getAll(App.profileId).observe(this, announcements -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
if (announcements == null) {
recyclerView.setVisibility(View.GONE);
b.announcementsNoData.setVisibility(View.VISIBLE);
return;
}
if (announcements.size() > 0) {
/*if ((adapter = (AnnouncementsAdapter) recyclerView.getAdapter()) != null) {
adapter.announcementList = announcements;
adapter.notifyDataSetChanged();
return;
}*/
AnnouncementsAdapter announcementsAdapter = new AnnouncementsAdapter(activity, announcements, (v, announcement) -> {
MaterialDialog dialog = new MaterialDialog.Builder(activity)
.title(announcement.subject)
.customView(R.layout.dialog_announcement, true)
.positiveText(R.string.ok)
.show();
DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView());
b.text.setText(announcement.teacherFullName+"\n\n"+ (announcement.startDate != null ? announcement.startDate.getFormattedString() : "-")+" do "+ (announcement.endDate != null ? announcement.endDate.getFormattedString() : "-")+"\n\n" +announcement.text);
if (!announcement.seen) {
announcement.seen = true;
AsyncTask.execute(() -> {
app.db.metadataDao().setSeen(App.profileId, announcement, true);
});
if (recyclerView.getAdapter() != null)
recyclerView.getAdapter().notifyDataSetChanged();
}
});
recyclerView.setAdapter(announcementsAdapter);
// NOTE: need to disable change animations to ripple effect work properly
//((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
//expMgr.attachRecyclerView(recyclerView);
recyclerView.setVisibility(View.VISIBLE);
b.announcementsNoData.setVisibility(View.GONE);
}
else {
recyclerView.setVisibility(View.GONE);
b.announcementsNoData.setVisibility(View.VISIBLE);
}
});
}
}

View File

@ -0,0 +1,128 @@
package pl.szczodrzynski.edziennik.ui.modules.attendance;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.AttendanceFull;
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;
public class AttendancesAdapter extends RecyclerView.Adapter<AttendancesAdapter.ViewHolder> {
private Context context;
public List<AttendanceFull> attendanceList;
//getting the context and product list with constructor
public AttendancesAdapter(Context mCtx, List<AttendanceFull> noticeList) {
this.context = mCtx;
this.attendanceList = noticeList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_attendance_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
AttendanceFull attendance = attendanceList.get(position);
holder.attendanceLessonTopic.setText(attendance.lessonTopic);
holder.attendanceTeacher.setText(attendance.teacherFullName);
holder.attendanceSubject.setText(attendance.subjectLongName);
holder.attendanceDate.setText(attendance.lessonDate.getStringDmy());
holder.attendanceTime.setText(attendance.startTime.getStringHM());
switch (attendance.type) {
case TYPE_ABSENT:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xfff44336, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_absent);
break;
case TYPE_ABSENT_EXCUSED:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffaeea00, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_absent_excused);
break;
case TYPE_BELATED:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffca28, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_belated);
break;
case TYPE_BELATED_EXCUSED:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff4bb733, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_belated_excused);
break;
case TYPE_RELEASED:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff9e9e9e, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_released);
break;
case TYPE_PRESENT:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffae00, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText(R.string.attendance_present);
break;
default:
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff03a9f4, PorterDuff.Mode.MULTIPLY));
holder.attendanceType.setText("?");
break;
}
if (!attendance.seen) {
holder.attendanceLessonTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
attendance.seen = true;
AsyncTask.execute(() -> {
app.db.metadataDao().setSeen(App.profileId, attendance, true);
//Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure");
//context.sendBroadcast(i);
});
}
else {
holder.attendanceLessonTopic.setBackground(null);
}
}
@Override
public int getItemCount() {
return attendanceList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView attendanceType;
TextView attendanceLessonTopic;
TextView attendanceSubject;
TextView attendanceTeacher;
TextView attendanceDate;
TextView attendanceTime;
ViewHolder(View itemView) {
super(itemView);
attendanceType = itemView.findViewById(R.id.attendanceType);
attendanceLessonTopic = itemView.findViewById(R.id.attendanceLessonTopic);
attendanceSubject = itemView.findViewById(R.id.attendanceSubject);
attendanceTeacher = itemView.findViewById(R.id.attendanceTeacher);
attendanceDate = itemView.findViewById(R.id.attendanceDate);
attendanceTime = itemView.findViewById(R.id.attendanceTime);
}
}
}

View File

@ -0,0 +1,363 @@
package pl.szczodrzynski.edziennik.ui.modules.attendance;
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.databinding.FragmentRegisterAttendancesBinding;
import pl.szczodrzynski.edziennik.datamodels.AttendanceFull;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.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)+"%";
};
}

View File

@ -0,0 +1,180 @@
package pl.szczodrzynski.edziennik.ui.modules.base;
/*
* Copyright 2014-2017 Eduard Ereza Martínez
*
* 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.
*/
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.text.Html;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
import cat.ereza.customactivityoncrash.config.CaocConfig;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.Themes;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
public final class CrashActivity extends AppCompatActivity {
private App app;
@SuppressLint("PrivateResource")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.app = (App)getApplication();
setTheme(Themes.INSTANCE.getAppTheme());
setContentView(R.layout.activity_crash);
final CaocConfig config = CustomActivityOnCrash.getConfigFromIntent(getIntent());
if (config == null) {
//This should never happen - Just finish the activity to avoid a recursive crash.
finish();
return;
}
//Close/restart button logic:
//If a class if set, use restart.
//Else, use close and just finish the app.
//It is recommended that you follow this logic if implementing a custom error activity.
Button restartButton = findViewById(R.id.crash_restart_btn);
restartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CustomActivityOnCrash.restartApplication(CrashActivity.this, config);
}
});
Button devMessageButton = findViewById(R.id.crash_dev_message_btn);
devMessageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(CrashActivity.this, CrashGtfoActivity.class);
startActivity(i);
}
});
final Button reportButton = findViewById(R.id.crash_report_btn);
reportButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!app.networkUtils.isOnline())
{
new MaterialDialog.Builder(CrashActivity.this)
.title(R.string.network_you_are_offline_title)
.content(R.string.network_you_are_offline_text)
.positiveText(R.string.ok)
.show();
}
else
{
//app.networkUtils.setSelfSignedSSL(CrashActivity.this, null);
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?report", "CrashActivity")
.setBodyParameter("base64_encoded", Base64.encodeToString(getErrorString(getIntent(), true).getBytes(), Base64.DEFAULT))
.run((e, result) -> {
if (result != null)
{
if (result.get("success").getAsBoolean()) {
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_sent), Toast.LENGTH_SHORT).show();
reportButton.setEnabled(false);
reportButton.setTextColor(getResources().getColor(android.R.color.darker_gray));
}
else {
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_cannot_send) + ": " + result.get("reason").getAsString(), Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_cannot_send)+" JsonObject equals null", Toast.LENGTH_LONG).show();
}
});
}
}
});
Button moreInfoButton = findViewById(R.id.crash_details_btn);
moreInfoButton.setOnClickListener(v -> new MaterialDialog.Builder(CrashActivity.this)
.title(R.string.crash_details)
.content(Html.fromHtml(getErrorString(getIntent(), false)))
.typeface(null, "RobotoMono-Regular.ttf")
.positiveText(R.string.close)
.neutralText(R.string.copy_to_clipboard)
.onNeutral((dialog, which) -> copyErrorToClipboard())
.show());
String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent());
if (errorInformation.contains("MANUAL CRASH"))
{
findViewById(R.id.crash_notice).setVisibility(View.GONE);
findViewById(R.id.crash_report_btn).setVisibility(View.GONE);
findViewById(R.id.crash_feature).setVisibility(View.VISIBLE);
}
else
{
findViewById(R.id.crash_notice).setVisibility(View.VISIBLE);
findViewById(R.id.crash_report_btn).setVisibility(View.VISIBLE);
findViewById(R.id.crash_feature).setVisibility(View.GONE);
}
}
private String getErrorString(Intent intent, boolean plain) {
// build a string containing the stack trace and the device name + user's registration data
String contentPlain = "Crash report:\n\n"+CustomActivityOnCrash.getStackTraceFromIntent(intent);
String content = "<small>"+contentPlain+"</small>";
content = content.replaceAll(getPackageName(), "<font color='#4caf50'>"+getPackageName()+"</font>");
content = content.replaceAll("\n", "<br>");
contentPlain += "\n"+Build.MANUFACTURER+"\n"+Build.BRAND+"\n"+Build.MODEL+"\n"+Build.DEVICE+"\n";
if (app.profile != null && app.profile.getRegistration() == REGISTRATION_ENABLED) {
contentPlain += "U: "+app.profile.getUsernameId()+"\nS: "+ app.profile.getStudentNameLong() +"\n";
}
contentPlain += BuildConfig.VERSION_NAME+" "+BuildConfig.BUILD_TYPE;
return plain ? contentPlain : content;
}
private void copyErrorToClipboard() {
String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent());
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
//Are there any devices without clipboard...?
if (clipboard != null) {
ClipData clip = ClipData.newPlainText(getString(R.string.customactivityoncrash_error_activity_error_details_clipboard_label), errorInformation);
clipboard.setPrimaryClip(clip);
Toast.makeText(CrashActivity.this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -0,0 +1,20 @@
package pl.szczodrzynski.edziennik.ui.modules.base;
import android.content.Context;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
public class CrashGtfoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme((((App)getApplication()).getContext()
.getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE)
.getBoolean("dark_theme", false) ? R.style.AppTheme_Dark : R.style.AppTheme));
setContentView(R.layout.activity_gtfo);
}
}

View File

@ -0,0 +1,232 @@
package pl.szczodrzynski.edziennik.ui.modules.base;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.material.textfield.TextInputEditText;
import com.google.gson.Gson;
import com.yuyh.jsonviewer.library.JsonRecyclerView;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.utils.Themes;
public class DebugFragment extends Fragment {
App app;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
app = (App)getActivity().getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_debug, container, false);
}
private static class SimpleObj {
Object value;
}
private static Object target;
private static Field targetField = null;
private static Object temp;
private static Object getByPath(App app, Object parent, String path, boolean setGlobal) throws NoSuchMethodException, NoSuchFieldException, InvocationTargetException, IllegalAccessException {
Object target = parent;
Field targetField = null;
List<String> pathList = new ArrayList<>();
Matcher matcher = Pattern.compile("(.+?(?:\\(.*?\\))?)\\.").matcher(path+".");
while (matcher.find()) {
pathList.add(matcher.group(1));
}
for (String el: pathList) {
if (targetField != null) {
target = targetField.get(target);
targetField = null;
}
if (el.equals("temp")) {
target = temp;
continue;
}
if (el.endsWith(")")) {
String methodName = el.substring(0, el.indexOf("("));
String parameters = el.substring(el.indexOf("(")+1, el.lastIndexOf(")"));
Class[] paramTypes = new Class[]{};
Object[] paramValues = new Object[]{};
if (!parameters.isEmpty()) {
List<Class> paramTypeList = new ArrayList<>();
List<Object> paramValueList = new ArrayList<>();
for (String parameter: parameters.split(",\\s?")) {
if (parameter.equals("null")) {
paramTypeList.add(null);
paramValueList.add(null);
}
if (parameter.equals("temp") || (parameter.contains(".") && !parameter.startsWith("\""))) {
// this parameter is a path
if (parameter.startsWith("app.")) {
parameter = parameter.replaceFirst("app\\.", "");
}
Object param = getByPath(app, app, parameter, false);
paramTypeList.add(param.getClass());
paramValueList.add(param);
continue;
}
String forceType = null;
if (parameter.toLowerCase().endsWith("d")) {
forceType = "d";
parameter = parameter.substring(0, parameter.length()-1);
} else if (parameter.toLowerCase().endsWith("l")) {
forceType = "l";
parameter = parameter.substring(0, parameter.length()-1);
} else if (parameter.toLowerCase().endsWith("f")) {
forceType = "f";
parameter = parameter.substring(0, parameter.length()-1);
} else if (parameter.toLowerCase().endsWith("i")) {
forceType = "i";
parameter = parameter.substring(0, parameter.length()-1);
}
SimpleObj obj = app.gson.fromJson("{\"value\":"+parameter+"}", SimpleObj.class);
Class type = obj.value.getClass();
Object value = obj.value;
if ("d".equals(forceType)) {
value = Double.valueOf(parameter);
type = Double.TYPE;
} else if ("l".equals(forceType)) {
value = Long.valueOf(parameter);
type = Long.TYPE;
} else if ("f".equals(forceType)) {
value = Float.valueOf(parameter);
type = Float.TYPE;
} else if ("i".equals(forceType)) {
value = Integer.valueOf(parameter);
type = Integer.TYPE;
}
paramTypeList.add(type);
paramValueList.add(value);
}
paramTypes = paramTypeList.toArray(new Class[0]);
paramValues = paramValueList.toArray(new Object[0]);
}
try {
Method method = target.getClass().getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
target = method.invoke(target, paramValues);
}
catch (NoSuchMethodException e) {
Method method = target.getClass().getMethod(methodName, paramTypes);
target = method.invoke(target, paramValues);
}
}
else {
try {
targetField = target.getClass().getDeclaredField(el);
targetField.setAccessible(true);
}
catch (NoSuchFieldException e) {
targetField = target.getClass().getField(el);
}
}
}
if (setGlobal) {
DebugFragment.target = target;
DebugFragment.targetField = targetField;
}
return target;
}
public static String runCommand(App app, String cmdBatch) {
for (String cmd: cmdBatch.split(";(?:\\r|\\n|\\r\\n|\\n\\r)?")) {
cmd = cmd.trim();
if (cmd.isEmpty())
continue;
String[] data;
String path;
String valueToSet;
if (cmd.contains(" = ")) {
data = cmd.replaceFirst("run ", "").split(" = ");
path = data[0];
valueToSet = data[1];
} else {
path = cmd.replaceFirst("run ", "");
valueToSet = null;
}
target = app;
try {
boolean setToTemp = false;
if (path.startsWith("temp=")) {
path = path.replaceFirst("temp=", "");
setToTemp = true;
}
if (path.startsWith("app.")) {
path = path.replaceFirst("app\\.", "");
}
getByPath(app, target, path, true);
// set the value if specified
if (valueToSet != null) {
targetField.set(target, app.gson.fromJson(valueToSet, targetField.getGenericType()));
targetField = null;
}
if (targetField != null) {
target = targetField.get(target);
}
if (setToTemp) {
temp = target;
}
} catch (Exception e) {
e.printStackTrace();
return Log.getStackTraceString(e);
}
}
return app.gson.toJson(target);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
getView().findViewById(R.id.debugRegister).setOnClickListener(v -> {
String text = ((TextInputEditText) getView().findViewById(R.id.runText)).getText().toString();
JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json);
String result = runCommand(app, "run " + text);
try {
mRecyclerView.bindJson(result);
}
catch (Exception e) {
new MaterialDialog.Builder(getActivity())
.title("Result")
.content(result)
.positiveText(R.string.ok)
.show();
}
mRecyclerView.setTextSize(20);
});
getView().findViewById(R.id.debugAppconfig).setOnClickListener(v -> {
JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json);
// bind json
mRecyclerView.bindJson(new Gson().toJson(app.appConfig));
mRecyclerView.setTextSize(20);
});
getView().findViewById(R.id.debugAppprofile).setOnClickListener(v -> {
JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json);
// bind json
mRecyclerView.bindJson(new Gson().toJson(app.profile));
mRecyclerView.setTextSize(20);
});
}
}

View File

@ -0,0 +1,363 @@
package pl.szczodrzynski.edziennik.ui.modules.base;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.ActivityFeedbackBinding;
import pl.szczodrzynski.edziennik.datamodels.FeedbackMessage;
import pl.szczodrzynski.edziennik.datamodels.FeedbackMessageWithCount;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.Animation;
import android.widget.PopupMenu;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.github.bassaer.chatmessageview.model.IChatUser;
import com.github.bassaer.chatmessageview.model.Message;
import com.github.bassaer.chatmessageview.view.ChatView;
import java.util.Calendar;
import java.util.List;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.utils.Utils.crc16;
import static pl.szczodrzynski.edziennik.utils.Utils.openUrl;
public class FeedbackActivity extends AppCompatActivity {
private static final String TAG = "FeedbackActivity";
private App app;
private ActivityFeedbackBinding b;
private boolean firstSend = true;
private String deviceToSend = null;
private String nameToSend = null;
private BroadcastReceiver receiver;
private class User implements IChatUser {
Integer id;
String name;
Bitmap icon;
public User(int id, String name, Bitmap icon) {
this.id = id;
this.name = name;
this.icon = icon;
}
@Override
public String getId() {
return this.id.toString();
}
@Override
public String getName() {
return this.name;
}
@Override
public Bitmap getIcon() {
return this.icon;
}
@Override
public void setIcon(Bitmap icon) {
this.icon = icon;
}
}
private User dev;
private User user;
private ChatView mChatView;
private void send(String text){
/*if ("enable dev mode pls".equals(text)) {
try {
Log.d(TAG, Utils.AESCrypt.encrypt("ok here you go it's enabled now", "8iryqZUfIUiLmJGi"));
} catch (Exception e) {
e.printStackTrace();
}
return;
}*/
MaterialDialog progressDialog = new MaterialDialog.Builder(this)
.title(R.string.loading)
.content(R.string.sending_message)
.negativeText(R.string.cancel)
.show();
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?feedback_message", "FeedbackSend")
.setBodyParameter("message_text", text)
.setBodyParameter("target_device", deviceToSend == null ? "null" : deviceToSend)
.run(((e, result) -> {
progressDialog.dismiss();
if (result != null && result.get("success") != null && result.get("success").getAsBoolean()) {
FeedbackMessage feedbackMessage = new FeedbackMessage(false, text);
if (deviceToSend != null) {
feedbackMessage.fromUser = deviceToSend;
feedbackMessage.fromUserName = nameToSend;
}
AsyncTask.execute(() -> app.db.feedbackMessageDao().add(feedbackMessage));
Message message = new Message.Builder()
.setUser(user)
.setRight(true)
.setText(feedbackMessage.text)
.hideIcon(true)
.build();
mChatView.send(message);
mChatView.setInputText("");
b.textInput.setText("");
if (firstSend) {
Anim.fadeOut(b.inputLayout, 500, new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
b.inputLayout.setVisibility(View.GONE);
Anim.fadeIn(b.chatLayout, 500, null);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
if (deviceToSend == null) {
// we are not the developer
FeedbackMessage feedbackMessage2 = new FeedbackMessage(true, "Postaram się jak najszybciej Tobie odpowiedzieć. Dostaniesz powiadomienie o odpowiedzi, która pokaże się w tym miejscu.");
AsyncTask.execute(() -> app.db.feedbackMessageDao().add(feedbackMessage2));
message = new Message.Builder()
.setUser(dev)
.setRight(false)
.setText(feedbackMessage2.text)
.hideIcon(false)
.build();
mChatView.receive(message);
}
firstSend = false;
}
}
else {
Toast.makeText(app, "Nie udało się wysłać wiadomości.", Toast.LENGTH_SHORT).show();
}
}));
}
private void openFaq() {
openUrl(this, "http://szkolny.eu/pomoc/");
new MaterialDialog.Builder(this)
.title(R.string.faq_back_title)
.content(R.string.faq_back_text)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> {
}))
.onNegative(((dialog, which) -> {
}))
.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(Themes.INSTANCE.getAppTheme());
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_feedback, null, false);
setContentView(b.getRoot());
app = (App) getApplication();
setSupportActionBar(b.toolbar);
if (getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
b.faqText.setOnClickListener((v -> {
openFaq();
}));
b.faqButton.setOnClickListener((v -> {
openFaq();
}));
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
FeedbackMessage message = app.gson.fromJson(intent.getStringExtra("message"), FeedbackMessage.class);
Calendar c = Calendar.getInstance();
c.setTimeInMillis(message.sentTime);
Message chatMessage = new Message.Builder()
.setUser(intent.getStringExtra("type").equals("dev_chat") ? new User(crc16(message.fromUser.getBytes()), message.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : dev)
.setRight(!message.received)
.setText(message.text)
.setSendTime(c)
.hideIcon(!message.received)
.build();
if (message.received)
mChatView.receive(chatMessage);
else
mChatView.send(chatMessage);
}
};
mChatView = b.chatView;
dev = new User(0, "Szkolny.eu", BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
user = new User(1, "Ja", BitmapFactory.decodeResource(getResources(), R.drawable.profile));
//Set UI parameters if you need
mChatView.setLeftBubbleColor(Utils.getAttr(this, R.attr.colorSurface));
mChatView.setLeftMessageTextColor(Utils.getAttr(this, android.R.attr.textColorPrimary));
mChatView.setRightBubbleColor(Utils.getAttr(this, R.attr.colorPrimary));
mChatView.setRightMessageTextColor(Color.WHITE);
//mChatView.setBackgroundColor(ContextCompat.getColor(this, R.color.blueGray500));
mChatView.setSendButtonColor(Utils.getAttr(this, R.attr.colorAccent));
mChatView.setSendIcon(R.drawable.ic_action_send);
//mChatView.setUsernameTextColor(Color.WHITE);
//mChatView.setSendTimeTextColor(Color.WHITE);
//mChatView.setDateSeparatorColor(Color.WHITE);
mChatView.setInputTextHint("Napisz...");
//mChatView.setInputTextColor(Color.BLACK);
mChatView.setMessageMarginTop(5);
mChatView.setMessageMarginBottom(5);
if (App.devMode && app.deviceId.equals("f054761fbdb6a238")) {
b.targetDeviceLayout.setVisibility(View.VISIBLE);
b.targetDeviceDropDown.setOnClickListener((v -> {
AsyncTask.execute(() -> {
List<FeedbackMessageWithCount> messageList = app.db.feedbackMessageDao().getAllWithCountNow();
runOnUiThread(() -> {
PopupMenu popupMenu = new PopupMenu(this, b.targetDeviceDropDown);
int index = 0;
for (FeedbackMessageWithCount message: messageList) {
popupMenu.getMenu().add(0, index, index, message.fromUserName+" - "+message.fromUser+" ("+message.messageCount+")");
index++;
}
popupMenu.setOnMenuItemClickListener(item -> {
b.targetDeviceDropDown.setText(item.getTitle());
mChatView.getMessageView().removeAll();
FeedbackMessageWithCount message = messageList.get(item.getItemId());
deviceToSend = message.fromUser;
nameToSend = message.fromUserName;
AsyncTask.execute(() -> {
List<FeedbackMessage> messageList2 = app.db.feedbackMessageDao().getAllByUserNow(deviceToSend);
runOnUiThread(() -> {
b.chatLayout.setVisibility(View.VISIBLE);
b.inputLayout.setVisibility(View.GONE);
for (FeedbackMessage message2 : messageList2) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(message2.sentTime);
Message chatMessage = new Message.Builder()
.setUser(message2.received ? new User(crc16(message2.fromUser.getBytes()), message2.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : user)
.setRight(!message2.received)
.setText(message2.text)
.setSendTime(c)
.hideIcon(!message2.received)
.build();
if (message2.received)
mChatView.receive(chatMessage);
else
mChatView.send(chatMessage);
}
});
});
return false;
});
popupMenu.show();
});
});
}));
}
else {
AsyncTask.execute(() -> {
List<FeedbackMessage> messageList = app.db.feedbackMessageDao().getAllNow();
firstSend = messageList.size() == 0;
runOnUiThread(() -> {
if (firstSend) {
openFaq();
b.chatLayout.setVisibility(View.GONE);
b.inputLayout.setVisibility(View.VISIBLE);
b.sendButton.setOnClickListener((v -> {
if (b.textInput.getText() == null || b.textInput.getText().length() == 0) {
Toast.makeText(app, "Podaj treść wiadomości.", Toast.LENGTH_SHORT).show();
} else {
send(b.textInput.getText().toString());
}
}));
} else {
/*new MaterialDialog.Builder(this)
.title(R.string.faq)
.content(R.string.faq_text)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> {
openFaq();
}))
.show();*/
b.chatLayout.setVisibility(View.VISIBLE);
b.inputLayout.setVisibility(View.GONE);
}
for (FeedbackMessage message : messageList) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(message.sentTime);
Message chatMessage = new Message.Builder()
.setUser(message.fromUser != null ? new User(crc16(message.fromUser.getBytes()), message.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : message.received ? dev : user)
.setRight(!message.received)
.setText(message.text)
.setSendTime(c)
.hideIcon(!message.received)
.build();
if (message.received)
mChatView.receive(chatMessage);
else
mChatView.send(chatMessage);
}
});
});
}
//Click Send Button
mChatView.setOnClickSendButtonListener(view -> {
send(mChatView.getInputText());
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) // Press Back Icon
{
finish();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, new IntentFilter("pl.szczodrzynski.edziennik.ui.modules.base.FeedbackActivity"));
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
}

View File

@ -0,0 +1,334 @@
package pl.szczodrzynski.edziennik.ui.modules.base
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.widget.PopupMenu
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import com.github.bassaer.chatmessageview.model.IChatUser
import com.github.bassaer.chatmessageview.model.Message
import com.github.bassaer.chatmessageview.view.ChatView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding
import pl.szczodrzynski.edziennik.datamodels.FeedbackMessage
import pl.szczodrzynski.edziennik.network.ServerRequest
import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.crc16
import pl.szczodrzynski.edziennik.utils.Utils.openUrl
import java.util.*
class FeedbackFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentFeedbackBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentFeedbackBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
b.faqText.setOnClickListener { v -> openFaq() }
b.faqButton.setOnClickListener { v -> openFaq() }
receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val message = app.gson.fromJson(intent.getStringExtra("message"), FeedbackMessage::class.java)
val c = Calendar.getInstance()
c.timeInMillis = message.sentTime
val chatMessage = Message.Builder()
.setUser(
if (intent.getStringExtra("type") == "dev_chat")
User(crc16(message.fromUser.toByteArray()), message.fromUserName, BitmapFactory.decodeResource(resources, R.drawable.ic_account_circle))
else
dev)
.setRight(!message.received)
.setText(message.text)
.setSendTime(c)
.hideIcon(!message.received)
.build()
if (message.received)
mChatView.receive(chatMessage)
else
mChatView.send(chatMessage)
}
}
//Set UI parameters if you need
mChatView.setLeftBubbleColor(Utils.getAttr(activity, R.attr.colorSurface))
mChatView.setLeftMessageTextColor(Utils.getAttr(activity, android.R.attr.textColorPrimary))
mChatView.setRightBubbleColor(Utils.getAttr(activity, R.attr.colorPrimary))
mChatView.setRightMessageTextColor(Color.WHITE)
//mChatView.setBackgroundColor(ContextCompat.getColor(this, R.color.blueGray500));
mChatView.setSendButtonColor(Utils.getAttr(activity, R.attr.colorAccent))
mChatView.setSendIcon(R.drawable.ic_action_send)
//mChatView.setUsernameTextColor(Color.WHITE);
//mChatView.setSendTimeTextColor(Color.WHITE);
//mChatView.setDateSeparatorColor(Color.WHITE);
mChatView.setInputTextHint("Napisz...")
//mChatView.setInputTextColor(Color.BLACK);
mChatView.setMessageMarginTop(5)
mChatView.setMessageMarginBottom(5)
if (App.devMode && app.deviceId == "f054761fbdb6a238") {
b.targetDeviceLayout.visibility = View.VISIBLE
b.targetDeviceDropDown.setOnClickListener { v ->
AsyncTask.execute {
val messageList = app.db.feedbackMessageDao().allWithCountNow
activity.runOnUiThread {
val popupMenu = PopupMenu(activity, b.targetDeviceDropDown)
var index = 0
for (message in messageList) {
popupMenu.menu.add(0, index, index, message.fromUserName + " - " + message.fromUser + " (" + message.messageCount + ")")
index++
}
popupMenu.setOnMenuItemClickListener { item ->
b.targetDeviceDropDown.setText(item.title)
mChatView.getMessageView().removeAll()
val message = messageList[item.itemId]
deviceToSend = message.fromUser
nameToSend = message.fromUserName
AsyncTask.execute {
val messageList2 = app.db.feedbackMessageDao().getAllByUserNow(deviceToSend)
activity.runOnUiThread {
b.chatLayout.visibility = View.VISIBLE
b.inputLayout.visibility = View.GONE
for (message2 in messageList2) {
val c = Calendar.getInstance()
c.timeInMillis = message2.sentTime
val chatMessage = Message.Builder()
.setUser(if (message2.received) User(crc16(message2.fromUser.toByteArray()), message2.fromUserName, BitmapFactory.decodeResource(resources, R.drawable.ic_account_circle)) else user)
.setRight(!message2.received)
.setText(message2.text)
.setSendTime(c)
.hideIcon(!message2.received)
.build()
if (message2.received)
mChatView.receive(chatMessage)
else
mChatView.send(chatMessage)
}
}
}
false
}
popupMenu.show()
}
}
}
} else {
AsyncTask.execute {
val messageList = app.db.feedbackMessageDao().allNow
firstSend = messageList.size == 0
activity.runOnUiThread {
if (firstSend) {
openFaq()
b.chatLayout.visibility = View.GONE
b.inputLayout.visibility = View.VISIBLE
b.sendButton.setOnClickListener { v ->
if (b.textInput.text == null || b.textInput.text!!.length == 0) {
Toast.makeText(app, "Podaj treść wiadomości.", Toast.LENGTH_SHORT).show()
} else {
send(b.textInput.text!!.toString())
}
}
} else {
/*new MaterialDialog.Builder(this)
.title(R.string.faq)
.content(R.string.faq_text)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> {
openFaq();
}))
.show();*/
b.chatLayout.visibility = View.VISIBLE
b.inputLayout.visibility = View.GONE
}
for (message in messageList) {
val c = Calendar.getInstance()
c.timeInMillis = message.sentTime
val chatMessage = Message.Builder()
.setUser(if (message.fromUser != null) User(crc16(message.fromUser.toByteArray()), message.fromUserName, BitmapFactory.decodeResource(resources, R.drawable.ic_account_circle)) else if (message.received) dev else user)
.setRight(!message.received)
.setText(message.text)
.setSendTime(c)
.hideIcon(!message.received)
.build()
if (message.received)
mChatView.receive(chatMessage)
else
mChatView.send(chatMessage)
}
}
}
}
//Click Send Button
mChatView.setOnClickSendButtonListener(View.OnClickListener { send(mChatView.inputText) })
}
private var firstSend = true
private var deviceToSend: String? = null
private var nameToSend: String? = null
private var receiver: BroadcastReceiver? = null
class User(internal var id: Int?, internal var name: String, internal var icon: Bitmap) : IChatUser {
override fun getId(): String {
return this.id!!.toString()
}
override fun getName(): String? {
return this.name
}
override fun getIcon(): Bitmap? {
return this.icon
}
override fun setIcon(icon: Bitmap) {
this.icon = icon
}
}
private val dev: User by lazy {
User(0, "Szkolny.eu", BitmapFactory.decodeResource(activity.resources, R.mipmap.ic_splash))
}
private val user: User by lazy {
User(1, "Ja", BitmapFactory.decodeResource(activity.resources, R.drawable.profile_))
}
private val mChatView: ChatView by lazy {
b.chatView
}
private fun send(text: String) {
/*if ("enable dev mode pls".equals(text)) {
try {
Log.d(TAG, Utils.AESCrypt.encrypt("ok here you go it's enabled now", "8iryqZUfIUiLmJGi"));
} catch (Exception e) {
e.printStackTrace();
}
return;
}*/
val progressDialog = MaterialDialog.Builder(activity)
.title(R.string.loading)
.content(R.string.sending_message)
.negativeText(R.string.cancel)
.show()
ServerRequest(app, app.requestScheme + APP_URL + "main.php?feedback_message", "FeedbackSend")
.setBodyParameter("message_text", text)
.setBodyParameter("target_device", if (deviceToSend == null) "null" else deviceToSend)
.run { e, result ->
progressDialog.dismiss()
if (result != null && result.get("success") != null && result.get("success").asBoolean) {
val feedbackMessage = FeedbackMessage(false, text)
if (deviceToSend != null) {
feedbackMessage.fromUser = deviceToSend
feedbackMessage.fromUserName = nameToSend
}
AsyncTask.execute { app.db.feedbackMessageDao().add(feedbackMessage) }
var message = Message.Builder()
.setUser(user!!)
.setRight(true)
.setText(feedbackMessage.text)
.hideIcon(true)
.build()
mChatView!!.send(message)
mChatView!!.inputText = ""
b.textInput.setText("")
if (firstSend) {
Anim.fadeOut(b.inputLayout, 500, object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
}
override fun onAnimationEnd(animation: Animation) {
b.inputLayout.visibility = View.GONE
Anim.fadeIn(b.chatLayout, 500, null)
}
override fun onAnimationRepeat(animation: Animation) {
}
})
if (deviceToSend == null) {
// we are not the developer
val feedbackMessage2 = FeedbackMessage(true, "Postaram się jak najszybciej Tobie odpowiedzieć. Dostaniesz powiadomienie o odpowiedzi, która pokaże się w tym miejscu.")
AsyncTask.execute { app.db.feedbackMessageDao().add(feedbackMessage2) }
message = Message.Builder()
.setUser(dev!!)
.setRight(false)
.setText(feedbackMessage2.text)
.hideIcon(false)
.build()
mChatView!!.receive(message)
}
firstSend = false
}
} else {
Toast.makeText(app, "Nie udało się wysłać wiadomości.", Toast.LENGTH_SHORT).show()
}
}
}
private fun openFaq() {
openUrl(activity, "http://szkolny.eu/pomoc/")
MaterialDialog.Builder(activity)
.title(R.string.faq_back_title)
.content(R.string.faq_back_text)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive { dialog, which ->
}
.onNegative { dialog, which ->
}
.show()
}
override fun onResume() {
super.onResume()
if (receiver != null)
activity.registerReceiver(receiver, IntentFilter("pl.szczodrzynski.edziennik.ui.modules.base.FeedbackActivity"))
}
override fun onPause() {
super.onPause()
if (receiver != null)
activity.unregisterReceiver(receiver)
}
}

View File

@ -0,0 +1,43 @@
package pl.szczodrzynski.edziennik.ui.modules.base
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentHelpBinding
import pl.szczodrzynski.edziennik.utils.Themes
class HelpFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentHelpBinding
/*
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentHelpBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
}
}

View File

@ -0,0 +1,23 @@
package pl.szczodrzynski.edziennik.ui.modules.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import pl.szczodrzynski.edziennik.databinding.FragmentLoadingBinding
import pl.szczodrzynski.edziennik.utils.Themes
class LoadingFragment : Fragment() {
private lateinit var b: FragmentLoadingBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (context == null)
return null
context!!.theme.applyStyle(Themes.appTheme, true)
// activity, context and profile is valid
b = FragmentLoadingBinding.inflate(inflater)
return b.root
}
}

View File

@ -0,0 +1,44 @@
package pl.szczodrzynski.edziennik.ui.modules.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.Navigation
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentTemplateBinding
import pl.szczodrzynski.edziennik.utils.Themes
class TemplateFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentTemplateBinding
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentTemplateBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
}
}

View File

@ -0,0 +1,144 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.Grade;
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.Colors;
import static pl.szczodrzynski.edziennik.datamodels.Profile.COLOR_MODE_DEFAULT;
public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.ViewHolder> {
private Context mContext;
private List<GradeFull> gradeList;
//getting the context and product list with constructor
public GradesListAdapter(Context mCtx, List<GradeFull> gradeList) {
this.mContext = mCtx;
this.gradeList = gradeList;
}
@NonNull
@Override
public GradesListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.row_grades_list_item, parent, false);
return new GradesListAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull GradesListAdapter.ViewHolder holder, int position) {
App app = (App) mContext.getApplicationContext();
GradeFull grade = gradeList.get(position);
holder.root.setOnClickListener((v -> {
new GradeDetailsDialog(v.getContext(), App.profileId).show(app, grade);
}));
int gradeColor;
if (app.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {
gradeColor = Colors.gradeToColor(grade);
}
holder.gradesListName.setText(grade.name);
holder.gradesListName.setSelected(true);
holder.gradesListName.setTypeface(null, Typeface.BOLD);
holder.gradesListName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
holder.gradesListName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
if (grade.description.trim().isEmpty()) {
holder.gradesListDescription.setText(grade.category);
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, "") : "");
}
else {
holder.gradesListDescription.setText(grade.description);
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, grade.category) : grade.category);
}
DecimalFormat format = new DecimalFormat("#.##");
DecimalFormat formatWithZeroes = new DecimalFormat("#.00");
if (grade.weight < 0) {
grade.weight *= -1;
}
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_BEHAVIOUR) {
holder.gradesListWeight.setVisibility(View.GONE);
grade.weight = 0;
}
else {
holder.gradesListWeight.setVisibility(View.VISIBLE);
if (grade.type == Grade.TYPE_POINT) {
holder.gradesListWeight.setText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
}
else if (grade.weight == 0) {
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_not_counted));
}
else {
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_format, format.format(grade.weight) + (grade.classAverage != -1 ? ", " + formatWithZeroes.format(grade.classAverage) : "")));
}
}
holder.gradesListTeacher.setText(grade.teacherFullName);
holder.gradesListAddedDate.setText(Date.fromMillis(grade.addedDate).getFormattedStringShort());
if (!grade.seen) {
holder.gradesListDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesListDescription.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesListDescription.setBackground(null);
}
}
@Override
public int getItemCount() {
return gradeList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
View root;
TextView gradesListName;
TextView gradesListDescription;
TextView gradesListCategory;
TextView gradesListWeight;
TextView gradesListTeacher;
TextView gradesListAddedDate;
ViewHolder(View itemView) {
super(itemView);
root = itemView.getRootView();
gradesListName = itemView.findViewById(R.id.gradesListName);
gradesListDescription = itemView.findViewById(R.id.gradesListCategoryColumn);
gradesListCategory = itemView.findViewById(R.id.gradesListCategoryDescription);
gradesListWeight = itemView.findViewById(R.id.gradesListWeight);
gradesListTeacher = itemView.findViewById(R.id.gradesListTeacher);
gradesListAddedDate = itemView.findViewById(R.id.gradesListAddedDate);
}
}
}

View File

@ -0,0 +1,785 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.graphics.ColorUtils;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.mikepenz.iconics.view.IconicsImageView;
import java.text.DecimalFormat;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.datamodels.AppDb;
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListAdapter;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Utils;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static pl.szczodrzynski.edziennik.MainActivity.TARGET_GRADES_EDITOR;
import static pl.szczodrzynski.edziennik.datamodels.Profile.COLOR_MODE_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_SEM;
public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> implements View.OnClickListener {
private static final String TAG = "GradesSubjectAdapter";
private MainActivity activity;
public List<ItemGradesSubjectModel> subjectList;
private void updateBadges(Context context, ItemGradesSubjectModel model) {
// do not need this since we have an Observer for unread counters..
//((App)getContext().getApplicationContext()).saveRegister(); // I don't like this.
}
private boolean invalidAvg(float avg) {
return avg == 0.0f || Float.isNaN(avg);
}
private void updateSubjectSemesterBadges(ViewHolder holder, ItemGradesSubjectModel model) {
if (model.semester1Unread > 0 || model.semester2Unread > 0) {
holder.gradesSubjectTitle.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectTitle.setBackground(null);
}
if (model.semester1Unread > 0) {
holder.gradesSubjectSemester1Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectSemester1Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectSemester1Title.setBackground(null);
}
if (model.semester2Unread > 0) {
holder.gradesSubjectSemester2Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.gradesSubjectSemester2Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.gradesSubjectSemester2Title.setBackground(null);
}
}
private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) {
boolean somethingChanged = false;
AppDb db = AppDb.getDatabase(null);
if (semester == 1) {
model.semester1Unread = 0;
for (GradeFull grade : model.grades1) {
if (!grade.seen) {
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true);
}
}
if (model.semester1Proposed != null && !model.semester1Proposed.seen)
db.metadataDao().setSeen(App.profileId, model.semester1Proposed, somethingChanged = true);
if (model.semester1Final != null && !model.semester1Final.seen)
db.metadataDao().setSeen(App.profileId, model.semester1Final, somethingChanged = true);
}
else if (semester == 2) {
model.semester2Unread = 0;
for (GradeFull grade : model.grades2) {
if (!grade.seen) {
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true);
}
}
if (model.semester2Proposed != null && !model.semester2Proposed.seen)
db.metadataDao().setSeen(App.profileId, model.semester2Proposed, somethingChanged = true);
if (model.semester2Final != null && !model.semester2Final.seen)
db.metadataDao().setSeen(App.profileId, model.semester2Final, somethingChanged = true);
if (model.yearProposed != null && !model.yearProposed.seen)
db.metadataDao().setSeen(App.profileId, model.yearProposed, somethingChanged = true);
if (model.yearFinal != null && !model.yearFinal.seen)
db.metadataDao().setSeen(App.profileId, model.yearFinal, somethingChanged = true);
}
if (somethingChanged) updateSubjectSemesterBadges(holder, model);
return somethingChanged;
}
private void expandSubject(ViewHolder holder, ItemGradesSubjectModel model) {
Anim.fadeOut(holder.gradesSubjectPreviewContainer, 200, new Animation.AnimationListener() {
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
boolean somethingChanged = false;
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
somethingChanged = gradesSetAsRead(holder, model, 1);
}
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
somethingChanged = gradesSetAsRead(holder, model, 2);
}
if (somethingChanged) updateBadges(getContext(), model);
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
Anim.expand(holder.gradesSubjectContent, 500, null);
}
});
}
private void collapseSubject(ViewHolder holder, ItemGradesSubjectModel model) {
Anim.collapse(holder.gradesSubjectContent, 500, new Animation.AnimationListener() {
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
@Override
public void onAnimationEnd(Animation animation) {
//holder.gradesSubjectPreviewContent.setVisibility(View.VISIBLE);
Anim.fadeIn(holder.gradesSubjectPreviewContainer, 200, null);
}
});
}
class BuildGradeViews extends AsyncTask<Void, Void, Void> {
boolean findViews;
ItemGradesSubjectModel model;
//ViewGroup parent;
//int position;
ViewHolder holder;
BuildGradeViews(ItemGradesSubjectModel model, ViewHolder holder, ViewGroup parent, int position, boolean findViews) {
this.model = model;
this.holder = holder;
this.findViews = findViews;
//this.parent = parent;
//this.position = position;
}
protected Void doInBackground(Void... params) {
if (this.findViews) {
findViews(holder, holder.gradesSubjectRoot);
}
return null;
}
protected void onPostExecute(Void aVoid) {
DecimalFormat df = new DecimalFormat("#.00");
if (this.findViews) {
// TODO NIE WIEM CO TO ROBI XD
//this.viewHolder.semestrTitle1.setText(C0193R.string.semestr1);
//this.viewHolder.semestrTitle2.setText(C0193R.string.semestr2);
/*if (GradesSubjectAdapter.this.columns == 0) {
DisplayMetrics metrics = GradeListAdapterBySubject.this.context.getResources().getDisplayMetrics();
if (GradeListAdapterBySubject.this.context.getResources().getBoolean(C0193R.bool.tablet)) {
GradeListAdapterBySubject.this.columns = ((int) ((((float) (this.parent.getWidth() / 2)) / metrics.density) - 30.0f)) / 58;
} else {
GradeListAdapterBySubject.this.columns = ((int) ((((float) this.parent.getWidth()) / metrics.density) - 30.0f)) / 58;
}
}
this.viewHolder.semestrGridView1.setColumnCount(GradeListAdapterBySubject.this.columns);
this.viewHolder.semestrGridView2.setColumnCount(GradeListAdapterBySubject.this.columns);*/
}
if (model != null && model.subject != null && model.subject.id != holder.lastSubject) {
holder.gradesSubjectRoot.setBackground(Colors.getAdaptiveBackgroundDrawable(model.subject.color, model.subject.color));
updateSubjectSemesterBadges(holder, model);
holder.gradesSubjectTitle.setText(model.subject.longName);
holder.gradesSubjectPreviewContent.removeAllViews();
if (model.expandView) {
// commented is without animations, do not use now (with unread badges)
//holder.gradesSubjectContent.setVisibility(View.VISIBLE);
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
expandSubject(holder, model);
model.expandView = false;
}
int showSemester = model.profile.getCurrentSemester();
List<GradeFull> gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
if (gradeList.size() == 0) {
showSemester = (showSemester == 1 ? 2 : 1);
gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
}
App app = (App) getContext().getApplicationContext();
float scale = getContext().getResources().getDisplayMetrics().density;
int _5dp = (int) (5 * scale + 0.5f);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
layoutParams.setMargins(0, 0, _5dp, 0);
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.appConfig.miniDrawerVisible ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
int totalWidthPx = 0;
boolean ellipsized = false;
if (showSemester != model.profile.getCurrentSemester()) {
// showing different semester, because of no grades in the selected one
holder.gradesSubjectPreviewSemester.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewSemester.setText(getContext().getString(R.string.grades_semester_header_format, showSemester));
// decrease the max preview width. DONE below
/*holder.gradesSubjectPreviewSemester.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewSemester.getMeasuredWidth();
maxWidthPx -= _5dp;*/
}
else {
holder.gradesSubjectPreviewSemester.setVisibility(View.GONE);
}
if (model.grades1.size() > 0) {
holder.gradesSubjectSemester1Nest.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester1Content.setHasFixedSize(false);
holder.gradesSubjectSemester1Content.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester1Content.setLayoutManager(new LinearLayoutManager(activity));
holder.gradesSubjectSemester1Content.setAdapter(new GradesListAdapter(activity, model.grades1));
holder.gradesSubjectSemester1Header.setVisibility(View.VISIBLE);
if (showSemester == 1) {
holder.gradesSubjectSemester1Container.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
holder.gradesSubjectSemester1Average.setVisibility(View.GONE);
}
else {
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Average.setVisibility(View.VISIBLE);
int formatSingle = (model.isPointSubject ? R.string.grades_average_single_percent_format : model.isBehaviourSubject ? R.string.grades_average_single_point_format : R.string.grades_average_single_format);
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
// PREVIEW AVERAGE
if (showSemester == 1) {
holder.gradesSubjectPreviewAverage.setText(
getContext().getString(
formatSingle,
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
)
);
}
// AVERAGE value
holder.gradesSubjectSemester1Average.setText(
getContext().getString(
format,
1,
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
)
);
}
// PROPOSED grade
if (model.semester1Proposed != null) {
holder.gradesSubjectSemester1Proposed.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Proposed.setText(model.semester1Proposed.name);
holder.gradesSubjectSemester1Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectSemester1Proposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
if (showSemester == 1) {
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewProposed.setText(model.semester1Proposed.name);
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
}
}
else {
holder.gradesSubjectSemester1Proposed.setVisibility(View.GONE);
if (showSemester == 1) {
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.semester1Final != null) {
holder.gradesSubjectSemester1Final.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1Final.setText(model.semester1Final.name);
holder.gradesSubjectSemester1Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 1) {
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewFinal.setText(model.semester1Final.name);
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectSemester1Final.setVisibility(View.GONE);
if (showSemester == 1) {
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
}
}
}
else {
holder.gradesSubjectSemester1Header.setVisibility(View.GONE);
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
}
if (model.grades2.size() > 0) {
holder.gradesSubjectSemester2Nest.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester2Content.setHasFixedSize(false);
holder.gradesSubjectSemester2Content.setNestedScrollingEnabled(false);
holder.gradesSubjectSemester2Content.setLayoutManager(new LinearLayoutManager(activity));
holder.gradesSubjectSemester2Content.setAdapter(new GradesListAdapter(activity, model.grades2));
holder.gradesSubjectSemester2Header.setVisibility(View.VISIBLE);
if (showSemester == 2) {
holder.gradesSubjectSemester2Container.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
holder.gradesSubjectSemester2Average.setVisibility(View.GONE);
}
else {
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Average.setVisibility(View.VISIBLE);
// PREVIEW AVERAGE
int formatDouble = (model.isPointSubject ? R.string.grades_average_double_percent_format : model.isBehaviourSubject ? R.string.grades_average_double_point_format : R.string.grades_average_double_format);
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
if (showSemester == 2) {
if (model.semester2Proposed != null || model.semester2Final != null) {
holder.gradesSubjectPreviewAverage.setText(invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average));
holder.gradesSubjectPreviewYearAverage.setText(invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage));
holder.gradesSubjectPreviewYearAverage.setVisibility(View.VISIBLE);
}
else {
holder.gradesSubjectPreviewAverage.setText(
getContext().getString(
formatDouble,
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average),
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
)
);
holder.gradesSubjectPreviewYearAverage.setVisibility(View.GONE);
}
}
// AVERAGE value
holder.gradesSubjectSemester2Average.setText(
getContext().getString(
format,
2,
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average)
)
);
}
// PROPOSED grade
if (model.semester2Proposed != null) {
holder.gradesSubjectSemester2Proposed.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Proposed.setText(model.semester2Proposed.name);
holder.gradesSubjectSemester2Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectSemester2Proposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
if (showSemester == 2) {
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewProposed.setText(model.semester2Proposed.name);
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
}
}
else {
holder.gradesSubjectSemester2Proposed.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.semester2Final != null) {
holder.gradesSubjectSemester2Final.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2Final.setText(model.semester2Final.name);
holder.gradesSubjectSemester2Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 2) {
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewFinal.setText(model.semester2Final.name);
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectSemester2Final.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
}
}
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
holder.gradesSubjectYearAverage.setVisibility(View.GONE);
}
else {
holder.gradesSubjectYearAverage.setVisibility(View.VISIBLE);
// AVERAGE value
int format = (model.isPointSubject ? R.string.grades_year_average_percent_format : model.isBehaviourSubject ? R.string.grades_year_average_point_format : R.string.grades_year_average_format);
holder.gradesSubjectYearAverage.setText(
getContext().getString(
format,
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
)
);
}
// PROPOSED grade
if (model.yearProposed != null) {
holder.gradesSubjectYearProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectYearProposed.setText(model.yearProposed.name);
holder.gradesSubjectYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
if (showSemester == 2) {
holder.gradesSubjectPreviewYearProposed.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewYearProposed.setText(model.yearProposed.name);
holder.gradesSubjectPreviewYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
holder.gradesSubjectPreviewYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
}
}
else {
holder.gradesSubjectYearProposed.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewYearProposed.setVisibility(View.GONE);
}
}
// FINAL grade
if (model.yearFinal != null) {
holder.gradesSubjectYearFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectYearFinal.setText(model.yearFinal.name);
holder.gradesSubjectYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
if (showSemester == 2) {
holder.gradesSubjectPreviewYearFinal.setVisibility(View.VISIBLE);
holder.gradesSubjectPreviewYearFinal.setText(model.yearFinal.name);
holder.gradesSubjectPreviewYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
}
}
else {
holder.gradesSubjectYearFinal.setVisibility(View.GONE);
if (showSemester == 2) {
holder.gradesSubjectPreviewYearFinal.setVisibility(View.GONE);
}
}
}
else {
holder.gradesSubjectSemester2Header.setVisibility(View.GONE);
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
}
// decrease the width by average, proposed, final and semester TextViews
holder.gradesSubjectPreviewContainer.measure(WRAP_CONTENT, MATCH_PARENT);
//Log.d(TAG, "gradesSubjectPreviewContainer "+holder.gradesSubjectPreviewContainer.getMeasuredWidth());
/*holder.gradesSubjectPreviewAverage.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewAverage.getMeasuredWidth();
maxWidthPx -= 2*_5dp;
if (holder.gradesSubjectPreviewProposed.getVisibility() == View.VISIBLE) {
holder.gradesSubjectPreviewProposed.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewProposed.getMeasuredWidth();
maxWidthPx -= _5dp;
}
if (holder.gradesSubjectPreviewFinal.getVisibility() == View.VISIBLE) {
holder.gradesSubjectPreviewFinal.measure(WRAP_CONTENT, WRAP_CONTENT);
maxWidthPx -= holder.gradesSubjectPreviewFinal.getMeasuredWidth();
maxWidthPx -= _5dp;
}*/
maxWidthPx -= holder.gradesSubjectPreviewContainer.getMeasuredWidth();
maxWidthPx -= _5dp;
for (GradeFull grade: gradeList) {
if (grade.semester != showSemester)
continue;
int gradeColor;
if (model.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
} else {
gradeColor = Colors.gradeToColor(grade);
}
TextView gradeName = new TextView(activity);
gradeName.setText(grade.name);
gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
gradeName.setPadding(_5dp, 0, _5dp, 0);
gradeName.setBackgroundResource(R.drawable.bg_rounded_4dp);
gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
gradeName.setTypeface(null, Typeface.BOLD);
gradeName.measure(WRAP_CONTENT, WRAP_CONTENT);
totalWidthPx += gradeName.getMeasuredWidth() + _5dp;
//Log.d(TAG, "totalWidthPx " + totalWidthPx);
if (totalWidthPx >= maxWidthPx) {
if (ellipsized)
continue;
ellipsized = true;
TextView ellipsisText = new TextView(activity);
ellipsisText.setText(R.string.ellipsis);
ellipsisText.setTextAppearance(activity, R.style.NavView_TextView);
ellipsisText.setTypeface(null, Typeface.BOLD);
ellipsisText.setPadding(0, 0, 0, 0);
holder.gradesSubjectPreviewContent.addView(ellipsisText, layoutParams);
}
else {
holder.gradesSubjectPreviewContent.addView(gradeName, layoutParams);
}
}
}
if (findViews) {
//Log.d("GradesSubjectAdapter", "runOnUiThread");
//this.viewHolder.gradesSubjectContent.setTag(View.VISIBLE);
//this.viewHolder.gradesSubjectPreviewContainer.setTag(View.VISIBLE);
activity.runOnUiThread(() -> {
holder.gradesSubjectRoot.setOnClickListener(v -> {
if (holder.gradesSubjectContent.getVisibility() == View.GONE) {
expandSubject(holder, model);
}
else {
collapseSubject(holder, model);
}
});
holder.gradesSubjectSemester1Header.setOnClickListener(v -> {
if (holder.gradesSubjectSemester1Container.getVisibility() == View.GONE) {
if (gradesSetAsRead(holder, model, 1)) updateBadges(getContext(), model);
Anim.expand(holder.gradesSubjectSemester1Container, 500, null);
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
}
}
else {
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
}
});
holder.gradesSubjectSemester2Header.setOnClickListener(v -> {
if (holder.gradesSubjectSemester2Container.getVisibility() == View.GONE) {
if (gradesSetAsRead(holder, model, 2)) updateBadges(getContext(), model);
Anim.expand(holder.gradesSubjectSemester2Container, 500, null);
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
}
}
else {
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
}
});
// hide the grade simulator when there are point, behaviour or descriptive grades
if (model.isPointSubject || model.isBehaviourSubject || (model.isDescriptiveSubject && !model.isNormalSubject)) {
holder.gradesSubjectSemester1EditButton.setVisibility(View.GONE);
holder.gradesSubjectSemester2EditButton.setVisibility(View.GONE);
}
else {
holder.gradesSubjectSemester1EditButton.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester1EditButton.setOnClickListener(v -> {
Bundle arguments = new Bundle();
if (model.subject != null) {
arguments.putLong("subjectId", model.subject.id);
}
arguments.putInt("semester", 1);
//d(TAG, "Model is " + model);
switch (model.profile.getYearAverageMode()) {
case YEAR_1_SEM_2_AVG:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester2Final == null && model.profile.getYearAverageMode() == YEAR_1_AVG_2_SEM ? -1 : model.profile.getYearAverageMode());
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester2);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester2);
arguments.putFloat("averageOtherSemester", model.semester2Average);
arguments.putFloat("finalOtherSemester", model.semester2Final == null ? -1 : model.semester2Final.value);
break;
}
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
});
holder.gradesSubjectSemester2EditButton.setVisibility(View.VISIBLE);
holder.gradesSubjectSemester2EditButton.setOnClickListener(v -> {
Bundle arguments = new Bundle();
if (model.subject != null) {
arguments.putLong("subjectId", model.subject.id);
}
arguments.putInt("semester", 2);
//d(TAG, "Model is " + model);
switch (model.profile.getYearAverageMode()) {
case YEAR_1_AVG_2_SEM:
case YEAR_1_SEM_2_SEM:
arguments.putInt("averageMode", -1);
break;
default:
arguments.putInt("averageMode", model.semester1Final == null && model.profile.getYearAverageMode() == YEAR_1_SEM_2_AVG ? -1 : model.profile.getYearAverageMode());
arguments.putFloat("yearAverageBefore", model.yearAverage);
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester1);
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester1);
arguments.putFloat("averageOtherSemester", model.semester1Average);
arguments.putFloat("finalOtherSemester", model.semester1Final == null ? -1 : model.semester1Final.value);
break;
}
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
});
}
});
}
if (model != null && model.subject != null) {
holder.lastSubject = model.subject.id;
}
super.onPostExecute(aVoid);
}
}
//getting the context and product list with constructor
public GradesSubjectAdapter(List<ItemGradesSubjectModel> data, MainActivity context) {
super(context, R.layout.row_grades_subject_item, data);
this.activity = context;
this.subjectList = data;
}
@Override
public void onClick(View v) {
int position = (Integer) v.getTag();
Object object = getItem(position);
ItemGradesSubjectModel dataModel = (ItemGradesSubjectModel)object;
}
private void findViews(ViewHolder holder, View root) {
//holder.gradesSubjectRoot = root.findViewById(R.id.gradesSubjectRoot);
holder.gradesSubjectTitle = root.findViewById(R.id.gradesSubjectTitle);
holder.gradesSubjectExpandIndicator = root.findViewById(R.id.gradesSubjectExpandIndicator);
holder.gradesSubjectPreviewContainer = root.findViewById(R.id.gradesSubjectPreviewContainer);
holder.gradesSubjectPreviewSemester = root.findViewById(R.id.gradesSubjectPreviewSemester);
holder.gradesSubjectPreviewContent = root.findViewById(R.id.gradesSubjectPreviewContent);
holder.gradesSubjectPreviewAverage = root.findViewById(R.id.gradesSubjectPreviewAverage);
holder.gradesSubjectPreviewProposed = root.findViewById(R.id.gradesSubjectPreviewProposed);
holder.gradesSubjectPreviewFinal = root.findViewById(R.id.gradesSubjectPreviewFinal);
holder.gradesSubjectPreviewYearAverage = root.findViewById(R.id.gradesSubjectPreviewYearAverage);
holder.gradesSubjectPreviewYearProposed = root.findViewById(R.id.gradesSubjectPreviewYearProposed);
holder.gradesSubjectPreviewYearFinal = root.findViewById(R.id.gradesSubjectPreviewYearFinal);
holder.gradesSubjectContent = root.findViewById(R.id.gradesSubjectContent);
holder.gradesSubjectSemester1Header = root.findViewById(R.id.gradesSubjectSemester1Header);
holder.gradesSubjectSemester1Title = root.findViewById(R.id.gradesSubjectSemester1Title);
holder.gradesSubjectSemester1ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester1ExpandIndicator);
holder.gradesSubjectSemester1Average = root.findViewById(R.id.gradesSubjectSemester1Average);
holder.gradesSubjectSemester1Proposed = root.findViewById(R.id.gradesSubjectSemester1Proposed);
holder.gradesSubjectSemester1Final = root.findViewById(R.id.gradesSubjectSemester1Final);
holder.gradesSubjectSemester1EditButton = root.findViewById(R.id.gradesSubjectSemester1EditButton);
holder.gradesSubjectSemester1Container = root.findViewById(R.id.gradesSubjectSemester1Container);
holder.gradesSubjectSemester1Nest = root.findViewById(R.id.gradesSubjectSemester1Nest);
holder.gradesSubjectSemester1Content = root.findViewById(R.id.gradesSubjectSemester1Content);
holder.gradesSubjectSemester2Header = root.findViewById(R.id.gradesSubjectSemester2Header);
holder.gradesSubjectSemester2Title = root.findViewById(R.id.gradesSubjectSemester2Title);
holder.gradesSubjectSemester2ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester2ExpandIndicator);
holder.gradesSubjectSemester2Average = root.findViewById(R.id.gradesSubjectSemester2Average);
holder.gradesSubjectSemester2Proposed = root.findViewById(R.id.gradesSubjectSemester2Proposed);
holder.gradesSubjectSemester2Final = root.findViewById(R.id.gradesSubjectSemester2Final);
holder.gradesSubjectSemester2EditButton = root.findViewById(R.id.gradesSubjectSemester2EditButton);
holder.gradesSubjectSemester2Container = root.findViewById(R.id.gradesSubjectSemester2Container);
holder.gradesSubjectSemester2Nest = root.findViewById(R.id.gradesSubjectSemester2Nest);
holder.gradesSubjectSemester2Content = root.findViewById(R.id.gradesSubjectSemester2Content);
holder.gradesSubjectYearAverage = root.findViewById(R.id.gradesSubjectYearAverage);
holder.gradesSubjectYearProposed = root.findViewById(R.id.gradesSubjectYearProposed);
holder.gradesSubjectYearFinal = root.findViewById(R.id.gradesSubjectYearFinal);
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
ItemGradesSubjectModel model = subjectList.get(position);
if (model == null) {
//Toast.makeText(activity, "return convertView;", Toast.LENGTH_SHORT).show();
return convertView;
}
ViewHolder holder;
if (convertView == null) {
try {
convertView = LayoutInflater.from(activity).inflate(R.layout.row_grades_subject_item, parent, false);
holder = new ViewHolder();
holder.gradesSubjectRoot = convertView.findViewById(R.id.gradesSubjectRoot);
convertView.setTag(holder);
new BuildGradeViews(model, holder, parent, position, true).execute();
return convertView;
} catch (Exception e) {
return new View(getContext());
}
}
holder = (ViewHolder) convertView.getTag();
Subject subject = model.subject;
holder.gradesSubjectTitle.setText(subject != null ? subject.longName : "");
/*if (model.getNotSeen() > 0) {
viewHolder.notseen.setVisibility(0);
viewHolder.notseen.setText(model.getNotSeen() + "");
} else {
viewHolder.notseen.setVisibility(8);
}*/
new BuildGradeViews(model, holder, parent, position, false).execute();
return convertView;
}
public int getViewTypeCount() {
return getCount();
}
public int getItemViewType(int position) {
return position;
}
class ViewHolder {
long lastSubject;
TextView gradesSubjectTitle;
ConstraintLayout gradesSubjectRoot;
IconicsImageView gradesSubjectExpandIndicator;
LinearLayout gradesSubjectPreviewContainer;
TextView gradesSubjectPreviewSemester;
LinearLayout gradesSubjectPreviewContent;
TextView gradesSubjectPreviewAverage;
TextView gradesSubjectPreviewProposed;
TextView gradesSubjectPreviewFinal;
TextView gradesSubjectPreviewYearAverage;
TextView gradesSubjectPreviewYearProposed;
TextView gradesSubjectPreviewYearFinal;
LinearLayout gradesSubjectContent;
LinearLayout gradesSubjectSemester1Header;
TextView gradesSubjectSemester1Title;
IconicsImageView gradesSubjectSemester1ExpandIndicator;
TextView gradesSubjectSemester1Average;
TextView gradesSubjectSemester1Proposed;
TextView gradesSubjectSemester1Final;
IconicsImageView gradesSubjectSemester1EditButton;
LinearLayout gradesSubjectSemester1Container;
NestedScrollView gradesSubjectSemester1Nest;
RecyclerView gradesSubjectSemester1Content;
LinearLayout gradesSubjectSemester2Header;
TextView gradesSubjectSemester2Title;
IconicsImageView gradesSubjectSemester2ExpandIndicator;
TextView gradesSubjectSemester2Average;
TextView gradesSubjectSemester2Proposed;
TextView gradesSubjectSemester2Final;
IconicsImageView gradesSubjectSemester2EditButton;
LinearLayout gradesSubjectSemester2Container;
NestedScrollView gradesSubjectSemester2Nest;
RecyclerView gradesSubjectSemester2Content;
TextView gradesSubjectYearAverage;
TextView gradesSubjectYearProposed;
TextView gradesSubjectYearFinal;
}
}

View File

@ -0,0 +1,566 @@
package pl.szczodrzynski.edziennik.ui.modules.grades;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterGradesBinding;
import pl.szczodrzynski.edziennik.datamodels.Grade;
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_GRADE;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_AVG_2_AVG;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_AVG_2_SEM;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_AVG;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_SEM;
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_ALL_GRADES;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_DATE_ASC;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_DATE_DESC;
import static pl.szczodrzynski.edziennik.utils.models.AppConfig.ORDER_BY_SUBJECT_ASC;
public class RegisterGradesFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentRegisterGradesBinding b = 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_grades, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
b.refreshLayout.setNestedScrollingEnabled(true);
return b.getRoot();
}
ListView listView;
List<ItemGradesSubjectModel> subjectList;
private boolean sortModeChanged = false;
private String getRegisterCardAverageModeSubText() {
switch (app.profile.getYearAverageMode()) {
default:
case YEAR_1_AVG_2_AVG:
return getString(R.string.settings_register_avg_mode_0_short);
case YEAR_1_SEM_2_AVG:
return getString(R.string.settings_register_avg_mode_1_short);
case YEAR_1_AVG_2_SEM:
return getString(R.string.settings_register_avg_mode_2_short);
case YEAR_1_SEM_2_SEM:
return getString(R.string.settings_register_avg_mode_3_short);
case YEAR_ALL_GRADES:
return getString(R.string.settings_register_avg_mode_4_short);
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
/*activity.getBottomSheet().setToggleGroupEnabled(true);
activity.getBottomSheet().toggleGroupRemoveItems();
activity.getBottomSheet().setToggleGroupSelectionMode(NavBottomSheet.TOGGLE_GROUP_SORTING_ORDER);
activity.getBottomSheet().toggleGroupAddItem(0, getString(R.string.sort_by_date), (Drawable)null, SORT_MODE_DESCENDING);
activity.getBottomSheet().toggleGroupAddItem(1, getString(R.string.sort_by_subject), (Drawable)null, SORT_MODE_ASCENDING);
activity.getBottomSheet().setToggleGroupSortingOrderListener((id, sortMode) -> {
sortModeChanged = true;
if (id == 0 && sortMode == SORT_MODE_ASCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_DATE_ASC;
}
else if (id == 1 && sortMode == SORT_MODE_ASCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_ASC;
}
else if (id == 0 && sortMode == SORT_MODE_DESCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_DATE_DESC;
}
else if (id == 1 && sortMode == SORT_MODE_DESCENDING) {
app.appConfig.gradesOrderBy = ORDER_BY_SUBJECT_DESC;
}
return null;
});
activity.getBottomSheet().setToggleGroupTitle("Sortowanie");
activity.getBottomSheet().toggleGroupCheck(0);
activity.getBottomSheet().setOnCloseListener(() -> {
if (sortModeChanged) {
sortModeChanged = false;
activity.reloadTarget();
}
return null;
});*/
activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_averages)
.withDescription(R.string.menu_grades_averages_desc)
.withIcon(CommunityMaterial.Icon.cmd_chart_line)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
showAverages();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_color_mode)
.withIcon(CommunityMaterial.Icon2.cmd_palette)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_color_mode_title)
.items(R.array.dialog_grades_color_modes)
.itemsCallbackSingleChoice(app.profile.getGradeColorMode(), (dialog, view1, which, text) -> {
app.profile.setGradeColorMode(which);
app.profileSaveAsync();
activity.reloadTarget();
return true;
})
.show();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_sort_mode)
.withIcon(CommunityMaterial.Icon2.cmd_sort)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_sort_title)
.items(R.array.dialog_grades_sort_modes)
.itemsCallbackSingleChoice(app.appConfig.gradesOrderBy, (dialog, view1, which, text) -> {
app.appConfig.gradesOrderBy = which;
app.saveConfig("gradesOrderBy");
activity.reloadTarget();
return true;
})
.show();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_grades_average_mode)
.withDescription(getRegisterCardAverageModeSubText())
.withIcon(CommunityMaterial.Icon2.cmd_scale_balance)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
List<CharSequence> modeNames = new ArrayList<>();
modeNames.add(getString(R.string.settings_register_avg_mode_4));
modeNames.add(getString(R.string.settings_register_avg_mode_0));
modeNames.add(getString(R.string.settings_register_avg_mode_1));
modeNames.add(getString(R.string.settings_register_avg_mode_2));
modeNames.add(getString(R.string.settings_register_avg_mode_3));
List<Integer> modeIds = new ArrayList<>();
modeIds.add(YEAR_ALL_GRADES);
modeIds.add(YEAR_1_AVG_2_AVG);
modeIds.add(YEAR_1_SEM_2_AVG);
modeIds.add(YEAR_1_AVG_2_SEM);
modeIds.add(YEAR_1_SEM_2_SEM);
new MaterialDialog.Builder(activity)
.title(getString(R.string.settings_register_avg_mode_dialog_title))
.content(getString(R.string.settings_register_avg_mode_dialog_text))
.items(modeNames)
.itemsCallbackSingleChoice(modeIds.indexOf(app.profile.getYearAverageMode()), (dialog, itemView, which, text) -> {
app.profile.setYearAverageMode(modeIds.get(which));
app.profileSaveAsync();
activity.reloadTarget();
return true;
})
.show();
}),
new BottomSheetSeparatorItem(true),
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_GRADE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
activity.gainAttention();
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_GRADES, b.refreshLayout);
});*/
listView = b.gradesRecyclerView;
//listView.setHasFixedSize(true);
//listView.setLayoutManager(new LinearLayoutManager(getContext()));
long expandSubjectId = -1;
if (getArguments() != null) {
expandSubjectId = getArguments().getLong("gradesSubjectId", -1);
}
/*b.gradesSwipeLayout.setOnRefreshListener(() -> {
Toast.makeText(activity, "Works!", Toast.LENGTH_LONG).show();
// To keep animation for 4 seconds
new Handler().postDelayed(() -> {
// Stop animation (This will be after 3 seconds)
b.gradesSwipeLayout.setRefreshing(false);
}, 3000);
});*/
long finalExpandSubjectId = expandSubjectId;
String orderBy;
if (app.appConfig.gradesOrderBy == ORDER_BY_SUBJECT_ASC) {
orderBy = "subjectLongName ASC, addedDate DESC";
}
else if (app.appConfig.gradesOrderBy == ORDER_BY_DATE_DESC) {
orderBy = "addedDate DESC";
}
else if (app.appConfig.gradesOrderBy == ORDER_BY_DATE_ASC) {
orderBy = "addedDate ASC";
}
else {
orderBy = "subjectLongName DESC, addedDate DESC";
}
app.db.gradeDao().getAllOrderBy(App.profileId, orderBy).observe(this, grades -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
subjectList = new ArrayList<>();
// now we have all grades from the newest to the oldest
for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) {
model = new ItemGradesSubjectModel(app.profile, new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new ArrayList<>(), new ArrayList<>());//ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
subjectList.add(model);
if (model.subject != null && model.subject.id == finalExpandSubjectId) {
model.expandView = true;
}
}
if (!grade.seen && grade.semester == 1) {
model.semester1Unread++;
}
if (!grade.seen && grade.semester == 2) {
model.semester2Unread++;
}
// COUNT POINT GRADES
if (grade.type == Grade.TYPE_POINT) {
model.isPointSubject = true;
if (grade.semester == 1) {
model.gradeSumOverall += grade.value;
model.gradeCountOverall += grade.valueMax;
model.gradeSumSemester1 += grade.value;
model.gradeCountSemester1 += grade.valueMax;
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1 * 100;
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.gradeSumOverall += grade.value;
model.gradeCountOverall += grade.valueMax;
model.gradeSumSemester2 += grade.value;
model.gradeCountSemester2 += grade.valueMax;
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2 * 100;
model.grades2.add(grade);
}
}
else if (grade.type == Grade.TYPE_BEHAVIOUR) {
model.isBehaviourSubject = true;
if (grade.semester == 1) {
model.semester1Average += grade.value;
model.yearAverage += grade.value;
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.semester2Average += grade.value;
model.yearAverage += grade.value;
model.grades2.add(grade);
}
}
else if (grade.type == Grade.TYPE_NORMAL) {
model.isNormalSubject = true;
float weight = grade.weight;
if (weight < 0) {
// do not show *normal* grades with negative weight - these are historical grades - Iuczniowie
continue;
}
if (app.appConfig.dontCountZeroToAverage && grade.name.equals("0")) {
weight = 0;
}
float valueWeighted = grade.value * weight;
if (grade.semester == 1) {
model.gradeSumOverall += valueWeighted;
model.gradeCountOverall += weight;
model.gradeSumSemester1 += valueWeighted;
model.gradeCountSemester1 += weight;
model.semester1Average = model.gradeSumSemester1 / model.gradeCountSemester1;
if (grade.parentId == -1) {
// show only "current" grades - these which are not historical
model.grades1.add(grade);
}
}
if (grade.semester == 2) {
model.gradeSumOverall += valueWeighted;
model.gradeCountOverall += weight;
model.gradeSumSemester2 += valueWeighted;
model.gradeCountSemester2 += weight;
model.semester2Average = model.gradeSumSemester2 / model.gradeCountSemester2;
if (grade.parentId == -1) {
// show only "current" grades - these which are not historical
model.grades2.add(grade);
}
}
}
else if (grade.type == Grade.TYPE_SEMESTER1_PROPOSED) {
model.semester1Proposed = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER1_FINAL) {
model.semester1Final = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER2_PROPOSED) {
model.semester2Proposed = grade;
}
else if (grade.type == Grade.TYPE_SEMESTER2_FINAL) {
model.semester2Final = grade;
}
else if (grade.type == Grade.TYPE_YEAR_PROPOSED) {
model.yearProposed = grade;
}
else if (grade.type == Grade.TYPE_YEAR_FINAL) {
model.yearFinal = grade;
}
else {
// descriptive grades, text grades
model.isDescriptiveSubject = true;
if (grade.semester == 1) {
model.grades1.add(grade);
}
if (grade.semester == 2) {
model.grades2.add(grade);
}
}
}
for (ItemGradesSubjectModel model: subjectList) {
if (model.isPointSubject) {
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall * 100.0f; // map the point grade "average" % value from 0.0f-1.0f to 0%-100%
}
/*else if (model.isDescriptiveSubject && !model.isNormalSubject) {
// applies for only descriptive grades - do nothing. average is hidden
//model.isDescriptiveSubject = false;
//model.semester1Average = -1;
//model.semester2Average = -1;
//model.yearAverage = -1;
}*/
else if (!model.isBehaviourSubject && model.isNormalSubject) {
// applies for normal grades & normal+descriptive grades
// calculate the normal grade average based on the user's setting
switch (app.profile.getYearAverageMode()) {
case YEAR_1_AVG_2_AVG:
model.yearAverage = (model.semester1Average + model.semester2Average) / 2;
break;
case YEAR_1_SEM_2_AVG:
model.yearAverage = model.semester1Final != null ? (model.semester1Final.value + model.semester2Average) / 2 : 0.0f;
break;
case YEAR_1_AVG_2_SEM:
model.yearAverage = model.semester2Final != null ? (model.semester1Average + model.semester2Final.value) / 2 : 0.0f;
break;
case YEAR_1_SEM_2_SEM:
model.yearAverage = model.semester1Final != null && model.semester2Final != null ? (model.semester1Final.value + model.semester2Final.value) / 2 : 0.0f;
break;
default:
case YEAR_ALL_GRADES:
model.yearAverage = model.gradeSumOverall / model.gradeCountOverall;
break;
}
}
}
if (subjectList.size() > 0) {
GradesSubjectAdapter adapter;
if ((adapter = (GradesSubjectAdapter) listView.getAdapter()) != null) {
adapter.subjectList = subjectList;
adapter.notifyDataSetChanged();
return;
}
adapter = new GradesSubjectAdapter(subjectList, activity);
listView.setAdapter(adapter);
listView.setVisibility(View.VISIBLE);
b.gradesNoData.setVisibility(View.GONE);
if (finalExpandSubjectId != -1) {
int subjectIndex = subjectList.indexOf(ItemGradesSubjectModel.searchModelBySubjectId(subjectList, finalExpandSubjectId));
listView.setSelection(subjectIndex > 0 ? subjectIndex - 1 : subjectIndex);
}
}
else {
listView.setVisibility(View.GONE);
b.gradesNoData.setVisibility(View.VISIBLE);
}
});
}
private int gradeFromAverage(float value) {
int grade = (int)Math.floor(value);
if (value % 1.0f >= 0.75f)
grade++;
return grade;
}
public void showAverages() {
if (app == null || app.profile == null || activity == null || b == null || !isAdded() || subjectList == null)
return;
float semester1Sum = 0;
float semester1Count = 0;
float semester1ProposedSum = 0;
float semester1ProposedCount = 0;
float semester1FinalSum = 0;
float semester1FinalCount = 0;
float semester2Sum = 0;
float semester2Count = 0;
float semester2ProposedSum = 0;
float semester2ProposedCount = 0;
float semester2FinalSum = 0;
float semester2FinalCount = 0;
float yearSum = 0;
float yearCount = 0;
float yearProposedSum = 0;
float yearProposedCount = 0;
float yearFinalSum = 0;
float yearFinalCount = 0;
for (ItemGradesSubjectModel subject: subjectList) {
// we cannot skip non-normal subjects because a point subject may also have a final grade
if (subject.isBehaviourSubject)
continue;
// SEMESTER 1 GRADES & AVERAGES
if (subject.semester1Final != null) { // if final available, add to final grades & expected grades
semester1FinalSum += subject.semester1Final.value;
semester1FinalCount++;
semester1Sum += subject.semester1Final.value;
semester1Count++;
}
else if (subject.semester1Proposed != null) { // if final not available, add proposed to expected grades
semester1Sum += subject.semester1Proposed.value;
semester1Count++;
}
else if (!Float.isNaN(subject.semester1Average)
&& subject.semester1Average > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
semester1Sum += gradeFromAverage(subject.semester1Average);
semester1Count++;
}
if (subject.semester1Proposed != null) { // add proposed to proposed grades even if final is available
semester1ProposedSum += subject.semester1Proposed.value;
semester1ProposedCount++;
}
// SEMESTER 2 GRADES & AVERAGES
if (subject.semester2Final != null) { // if final available, add to final grades & expected grades
semester2FinalSum += subject.semester2Final.value;
semester2FinalCount++;
semester2Sum += subject.semester2Final.value;
semester2Count++;
}
else if (subject.semester2Proposed != null) { // if final not available, add proposed to expected grades
semester2Sum += subject.semester2Proposed.value;
semester2Count++;
}
else if (!Float.isNaN(subject.semester2Average)
&& subject.semester2Average > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
semester2Sum += gradeFromAverage(subject.semester2Average);
semester2Count++;
}
if (subject.semester2Proposed != null) { // add proposed to proposed grades even if final is available
semester2ProposedSum += subject.semester2Proposed.value;
semester2ProposedCount++;
}
// YEAR GRADES & AVERAGES
if (subject.yearFinal != null) { // if final available, add to final grades & expected grades
yearFinalSum += subject.yearFinal.value;
yearFinalCount++;
yearSum += subject.yearFinal.value;
yearCount++;
}
else if (subject.yearProposed != null) { // if final not available, add proposed to expected grades
yearSum += subject.yearProposed.value;
yearCount++;
}
else if (!Float.isNaN(subject.yearAverage)
&& subject.yearAverage > 0
&& !subject.isPointSubject) { // if final&proposed unavailable, calculate from avg
yearSum += gradeFromAverage(subject.yearAverage);
yearCount++;
}
if (subject.yearProposed != null) { // add proposed to proposed grades even if final is available
yearProposedSum += subject.yearProposed.value;
yearProposedCount++;
}
}
DecimalFormat df = new DecimalFormat("0.00");
String semester1ExpectedAverageStr = semester1Count > semester1ProposedCount || semester1Count > semester1FinalCount ? getString(R.string.dialog_averages_expected_format, 1, semester1Sum / semester1Count) : "";
String semester1ProposedAverageStr = semester1ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 1, semester1ProposedSum / semester1ProposedCount) : "";
String semester1FinalAverageStr = semester1FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 1, semester1FinalSum / semester1FinalCount) : "";
String semester2ExpectedAverageStr = semester2Count > semester2ProposedCount || semester2Count > semester2FinalCount ? getString(R.string.dialog_averages_expected_format, 2, semester2Sum / semester2Count) : "";
String semester2ProposedAverageStr = semester2ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 2, semester2ProposedSum / semester2ProposedCount) : "";
String semester2FinalAverageStr = semester2FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 2, semester2FinalSum / semester2FinalCount) : "";
String yearExpectedAverageStr = yearCount > yearProposedCount || yearCount > yearFinalCount ? getString(R.string.dialog_averages_expected_yearly_format, yearSum / yearCount) : "";
String yearProposedAverageStr = yearProposedCount > 0 ? getString(R.string.dialog_averages_proposed_yearly_format, yearProposedSum / yearProposedCount) : "";
String yearFinalAverageStr = yearFinalCount > 0 ? getString(R.string.dialog_averages_final_yearly_format, yearFinalSum / yearFinalCount) : "";
if (semester1ExpectedAverageStr.isEmpty() && semester1ProposedAverageStr.isEmpty() && semester1FinalAverageStr.isEmpty()) {
semester1ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 1);
}
if (semester2ExpectedAverageStr.isEmpty() && semester2ProposedAverageStr.isEmpty() && semester2FinalAverageStr.isEmpty()) {
semester2ExpectedAverageStr = getString(R.string.dialog_averages_unavailable_format, 2);
}
if (yearExpectedAverageStr.isEmpty() && yearProposedAverageStr.isEmpty() && yearFinalAverageStr.isEmpty()) {
yearExpectedAverageStr = getString(R.string.dialog_averages_unavailable_yearly);
}
new MaterialDialog.Builder(activity)
.title(R.string.dialog_averages_title)
.content(getString(
R.string.dialog_averages_format,
semester1ExpectedAverageStr,
semester1ProposedAverageStr,
semester1FinalAverageStr,
semester2ExpectedAverageStr,
semester2ProposedAverageStr,
semester2FinalAverageStr,
yearExpectedAverageStr,
yearProposedAverageStr,
yearFinalAverageStr))
.positiveText(R.string.ok)
.show();
}
}

View File

@ -0,0 +1,118 @@
package pl.szczodrzynski.edziennik.ui.modules.grades.editor
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.RecyclerView
import com.daimajia.swipe.SwipeLayout
import com.mikepenz.iconics.view.IconicsImageView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment.Companion.modifyGradeChooser
import pl.szczodrzynski.edziennik.utils.Colors.gradeNameToColor
import java.text.DecimalFormat
class GradesEditorAdapter(
private val mContext: Context,
private val gradeList: List<GradesEditorFragment.EditorGrade>,
private val listener: OnGradeActionListener
) : RecyclerView.Adapter<GradesEditorAdapter.ViewHolder>() {
interface OnGradeActionListener {
fun onClickRemove(gradeId: Long)
fun onClickEdit(gradeId: Long)
fun onClickAdd()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
//inflating and returning our view holder
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.row_grades_editor_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val app = mContext.applicationContext as App
val editorGrade = gradeList[position]
holder.gradesListRoot.setOnClickListener { holder.swipeLayout.toggle() }
val gradeColor = gradeNameToColor(editorGrade.name)
holder.gradesListName.text = editorGrade.name
holder.gradesListName.isSelected = true
holder.gradesListName.setTypeface(null, Typeface.BOLD)
holder.gradesListName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.25) -0x1000000 else -0x1)
holder.gradesListName.background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
holder.gradesListCategory.text = editorGrade.category
if (editorGrade.weight < 0) {
editorGrade.weight *= -1f
}
if (editorGrade.weight == 0f) {
holder.gradesListWeight.text = app.getString(R.string.grades_weight_not_counted)
} else {
holder.gradesListWeight.text = app.getString(R.string.grades_weight_format, DecimalFormat("0.##").format(editorGrade.weight.toDouble()))
}
holder.gradesListValue.text = mContext.getString(R.string.grades_value_format, DecimalFormat("0.00").format(editorGrade.value.toDouble()))
holder.swipeLayout.showMode = SwipeLayout.ShowMode.LayDown
holder.swipeLayout.addDrag(SwipeLayout.DragEdge.Right, holder.bottomWrapper)
holder.swipeLayout.addSwipeListener(object : SwipeLayout.SwipeListener {
override fun onClose(layout: SwipeLayout) {
//when the SurfaceView totally cover the BottomView.
}
override fun onUpdate(layout: SwipeLayout, leftOffset: Int, topOffset: Int) {
//you are swiping.
}
override fun onStartOpen(layout: SwipeLayout) {
}
override fun onOpen(layout: SwipeLayout) {
//when the BottomView totally show.
}
override fun onStartClose(layout: SwipeLayout) {
}
override fun onHandRelease(layout: SwipeLayout, xvel: Float, yvel: Float) {
//when user's hand released.
}
})
holder.buttonRemove.setOnClickListener { listener.onClickRemove(editorGrade.id) }
holder.buttonEdit.setOnClickListener { v -> modifyGradeChooser(v, editorGrade) { listener.onClickEdit(editorGrade.id) } }
}
override fun getItemCount(): Int {
return gradeList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var gradesListRoot: ConstraintLayout = itemView.findViewById(R.id.gradesListRoot)
var gradesListName: TextView = itemView.findViewById(R.id.gradesListName)
var gradesListWeight: TextView = itemView.findViewById(R.id.gradesListWeight)
var gradesListValue: TextView = itemView.findViewById(R.id.gradesListValue)
var gradesListCategory: TextView = itemView.findViewById(R.id.gradesListCategory)
var swipeLayout: SwipeLayout = itemView.findViewById(R.id.swipeLayout)
var bottomWrapper: View = itemView.findViewById(R.id.bottom_wrapper)
var buttonRemove: IconicsImageView = itemView.findViewById(R.id.buttonRemove)
var buttonEdit: IconicsImageView = itemView.findViewById(R.id.buttonEdit)
}
}

View File

@ -0,0 +1,359 @@
package pl.szczodrzynski.edziennik.ui.modules.grades.editor
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
import pl.szczodrzynski.edziennik.datamodels.Grade
import pl.szczodrzynski.edziennik.datamodels.Profile.Companion.YEAR_1_AVG_2_AVG
import pl.szczodrzynski.edziennik.datamodels.Profile.Companion.YEAR_1_AVG_2_SEM
import pl.szczodrzynski.edziennik.datamodels.Profile.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.datamodels.Profile.Companion.YEAR_ALL_GRADES
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
import java.text.DecimalFormat
import java.util.*
import kotlin.math.floor
class GradesEditorFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentGradesEditorBinding
/*
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
private var subjectId: Long = -1
private var semester: Int = 1
private val editorGrades = ArrayList<EditorGrade>()
private lateinit var addingGrade: EditorGrade
private var gradeSumSemester = 0f
private var gradeCountSemester = 0f
private var averageSemester = 0f
private var gradeSumOtherSemester = 0f
private var gradeCountOtherSemester = 0f
private var averageOtherSemester = 0f
private var finalOtherSemester = 0.0f
private var averageMode = YEAR_ALL_GRADES // this means the container should be gone
private var yearAverageBefore = 0.0f
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentGradesEditorBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (app.profile == null || !isAdded)
return
subjectId = arguments.getLong("subjectId", -1)
semester = arguments.getInt("semester", 1)
if (subjectId == -1L) {
MaterialDialog.Builder(activity)
.title(R.string.error_occured)
.content(R.string.error_no_subject_id)
.positiveText(R.string.ok)
.onPositive { _, _ -> activity.navigateUp() }
.show()
return
}
averageMode = arguments.getInt("averageMode", YEAR_ALL_GRADES)
yearAverageBefore = arguments.getFloat("yearAverageBefore", -1f)
gradeSumOtherSemester = arguments.getFloat("gradeSumOtherSemester", -1f)
gradeCountOtherSemester = arguments.getFloat("gradeCountOtherSemester", -1f)
averageOtherSemester = arguments.getFloat("averageOtherSemester", -1f)
finalOtherSemester = arguments.getFloat("finalOtherSemester", -1f)
b.listView.setHasFixedSize(false)
b.listView.isNestedScrollingEnabled = false
b.listView.layoutManager = LinearLayoutManager(context)
b.listView.adapter = GradesEditorAdapter(context!!, editorGrades, object : GradesEditorAdapter.OnGradeActionListener {
override fun onClickRemove(gradeId: Long) {
gradeSumSemester = 0f
gradeCountSemester = 0f
averageSemester = 0f
var pos = 0
var removePos = 0
for (editorGrade in editorGrades) {
if (editorGrade.id == gradeId) {
removePos = pos
pos++
continue
}
var weight = editorGrade.weight
if (app.appConfig.dontCountZeroToAverage && editorGrade.name == "0") {
weight = 0f
}
val value = editorGrade.value * weight
gradeSumSemester += value
gradeCountSemester += weight//(value > 0 ? weight : 0);
pos++
}
editorGrades.removeAt(removePos)
averageSemester = gradeSumSemester / gradeCountSemester
refreshYearAverageAfter()
b.averageAfter.text = String.format(Locale.getDefault(), "%.02f", averageSemester)
var gradeInt = floor(averageSemester.toDouble()).toInt()
if (averageSemester % 1 >= 0.75)
gradeInt++
b.averageAfter.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
b.listView.adapter!!.notifyItemRemoved(removePos)
}
override fun onClickEdit(gradeId: Long) {
refreshDataset()
}
override fun onClickAdd() {
}
})
b.listView.visibility = View.VISIBLE
b.restoreGrades.setOnClickListener { refreshViews() }
b.addGrade.setOnClickListener { v ->
addingGrade = EditorGrade(System.currentTimeMillis(), "0", 0.0f, getString(R.string.grades_editor_new_grade), 0f)
addGradeHandler(v, addingGrade) {
editorGrades.add(0, addingGrade)
refreshDataset()
}
}
refreshViews()
}
private fun refreshYearAverageAfter() {
val yearAverage = when (averageMode) {
YEAR_ALL_GRADES -> (gradeSumOtherSemester + gradeSumSemester) / (gradeCountOtherSemester + gradeCountSemester)
YEAR_1_AVG_2_AVG -> (averageOtherSemester + averageSemester) / 2
YEAR_1_SEM_2_AVG, // the final grade is always the 'other'
YEAR_1_AVG_2_SEM -> (finalOtherSemester + averageSemester) / 2
else -> (gradeSumOtherSemester + gradeSumSemester) / (gradeCountOtherSemester + gradeCountSemester)
}
var gradeInt = floor(yearAverage.toDouble()).toInt()
if (yearAverage % 1 >= 0.75)
gradeInt++
b.yearAverageAfter.text = String.format(Locale.getDefault(), "%.02f", yearAverage)
b.yearAverageAfter.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
}
private fun refreshDataset() {
gradeSumSemester = 0f
gradeCountSemester = 0f
averageSemester = 0f
for (editorGrade in editorGrades) {
var weight = editorGrade.weight
if (app.appConfig.dontCountZeroToAverage && editorGrade.name == "0") {
weight = 0f
}
val value = editorGrade.value * weight
gradeSumSemester += value
gradeCountSemester += weight
}
averageSemester = gradeSumSemester / gradeCountSemester
refreshYearAverageAfter()
b.averageAfter.text = String.format(Locale.getDefault(), "%.02f", averageSemester)
var gradeInt = floor(averageSemester.toDouble()).toInt()
if (averageSemester % 1 >= 0.75)
gradeInt++
b.averageAfter.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
b.listView.adapter!!.notifyDataSetChanged()
}
private fun refreshViews() {
editorGrades.clear()
app.db.subjectDao().getById(App.profileId, subjectId).observe(this, Observer { subject ->
if (subject == null || subject.id == -1L) {
MaterialDialog.Builder(activity)
.title(R.string.error_occured)
.content(R.string.error_no_subject_id)
.positiveText(R.string.ok)
.onPositive { _, _ -> activity.navigateUp() }
.show()
return@Observer
}
gradeSumSemester = 0f
gradeCountSemester = 0f
averageSemester = 0f
app.db.gradeDao().getAllWhere(App.profileId, "subjectId = " + subject.id).observe(this, Observer { grades ->
for (grade in grades) {
if (grade.type == Grade.TYPE_NORMAL) {
if (grade.weight < 0) {
// do not show *normal* grades with negative weight - these are historical grades - Iuczniowie
continue
}
var weight = grade.weight
if (app.appConfig.dontCountZeroToAverage && grade.name == "0") {
weight = 0f
}
val value = grade.value * weight
if (grade.semester == semester) {
gradeSumSemester += value
gradeCountSemester += weight//(value > 0 ? weight : 0);
editorGrades.add(EditorGrade(grade.id, grade.name, grade.value, grade.description + " - " + grade.category, grade.weight))
}
}
}
averageSemester = gradeSumSemester / gradeCountSemester
if (averageMode == -1) {
b.yearAverageContainer.visibility = View.GONE
} else {
b.yearAverageContainer.visibility = View.VISIBLE
var gradeInt = floor(yearAverageBefore.toDouble()).toInt()
if (yearAverageBefore % 1 >= 0.75)
gradeInt++
b.yearAverageBefore.text = String.format(Locale.getDefault(), "%.02f", yearAverageBefore)
b.yearAverageBefore.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
refreshYearAverageAfter()
}
b.subjectName.text = subject.longName
b.semesterName.text = getString(R.string.grades_semester_header_format, semester)
var gradeInt = floor(averageSemester.toDouble()).toInt()
if (averageSemester % 1 >= 0.75)
gradeInt++
b.averageBefore.text = String.format(Locale.getDefault(), "%.02f", averageSemester)
b.averageBefore.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
b.averageAfter.text = String.format(Locale.getDefault(), "%.02f", averageSemester)
b.averageAfter.background.colorFilter = PorterDuffColorFilter(Colors.gradeNameToColor(gradeInt.toString()), PorterDuff.Mode.MULTIPLY)
b.listView.adapter!!.notifyDataSetChanged()
})
})
}
data class EditorGrade(
var id: Long,
var name: String,
var value: Float,
var category: String,
var weight: Float
)
companion object {
fun modifyGradeChooser(v: View, editorGrade: EditorGrade, callback: () -> Unit) {
val popup = PopupMenu(v.context, v)
popup.menu.add(0, 0, 0, R.string.grades_editor_change_grade)
popup.menu.add(0, 1, 1, R.string.grades_editor_change_weight)
popup.setOnMenuItemClickListener { item ->
if (item.itemId == 0) {
modifyGradeName(v, editorGrade, callback)
}
if (item.itemId == 1) {
modifyGradeWeight(v, editorGrade, callback)
}
true
}
popup.show()
}
fun addGradeHandler(v: View, editorGrade: EditorGrade, callback: () -> Unit) {
modifyGradeName(v, editorGrade) {
modifyGradeWeight(v, editorGrade, callback)
}
}
fun modifyGradeName(v: View, editorGrade: EditorGrade, callback: () -> Unit) {
val popup = PopupMenu(v.context, v)
popup.menu.add(0, 75, 0, "1-")
popup.menu.add(0, 100, 1, "1")
popup.menu.add(0, 150, 2, "1+")
popup.menu.add(0, 175, 3, "2-")
popup.menu.add(0, 200, 4, "2")
popup.menu.add(0, 250, 5, "2+")
popup.menu.add(0, 275, 6, "3-")
popup.menu.add(0, 300, 7, "3")
popup.menu.add(0, 350, 8, "3+")
popup.menu.add(0, 375, 9, "4-")
popup.menu.add(0, 400, 10, "4")
popup.menu.add(0, 450, 11, "4+")
popup.menu.add(0, 475, 12, "5-")
popup.menu.add(0, 500, 13, "5")
popup.menu.add(0, 550, 14, "5+")
popup.menu.add(0, 575, 15, "6-")
popup.menu.add(0, 600, 16, "6")
popup.menu.add(0, 650, 17, "6+")
popup.menu.add(0, 0, 18, "0")
popup.setOnMenuItemClickListener { item ->
editorGrade.name = item.title.toString()
editorGrade.value = item.itemId.toFloat() / 100
callback()
true
}
popup.show()
}
fun modifyGradeWeight(v: View, editorGrade: EditorGrade, callback: () -> Unit) {
val popup = PopupMenu(v.context, v)
for (i in 0..6) {
popup.menu.add(0, i, i, v.context.getString(R.string.grades_editor_weight_format, DecimalFormat("#.##").format(i.toLong())))
}
popup.menu.add(1, 100, 100, v.context.getString(R.string.grades_editor_weight_other))
popup.setOnMenuItemClickListener { item ->
if (item.itemId == 100) {
MaterialDialog.Builder(v.context)
.title(R.string.grades_editor_add_grade_title)
.content(R.string.grades_editor_add_grade_weight)
.inputType(InputType.TYPE_NUMBER_FLAG_SIGNED)
.input(null, null) { _, input ->
try {
editorGrade.weight = input.toString().toFloat()
callback()
} catch (e: Exception) {
e.printStackTrace()
}
}
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.show()
} else {
editorGrade.weight = item.itemId.toFloat()
callback()
}
true
}
popup.show()
}
}
}

View File

@ -0,0 +1,240 @@
package pl.szczodrzynski.edziennik.ui.modules.home;
import androidx.databinding.DataBindingUtil;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.ActivityCounterBinding;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import static pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment.updateInterval;
public class CounterActivity extends AppCompatActivity {
private static final String TAG = "CounterActivity";
private App app;
private ActivityCounterBinding b;
Timer timetableTimer;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (App) getApplication();
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_counter, null, false);
setContentView(b.getRoot());
timetableTimer = new Timer();
update();
}
private List<LessonFull> lessons = new ArrayList<>();
private void update() {
// BELL SYNCING
Time now = Time.getNow();
Time syncedNow = now;
//Time updateDiff = null;
if (app.appConfig.bellSyncDiff != null) {
if (app.appConfig.bellSyncMultiplier < 0) {
// the bell is too fast, need to step further to go with it
// add some time
syncedNow = Time.sum(now, app.appConfig.bellSyncDiff);
//Toast.makeText(c, "Bell sync diff is "+app.appConfig.bellSyncDiff.getStringHMS()+"\n\n Synced now is "+syncedNow.getStringHMS(), Toast.LENGTH_LONG).show();
}
if (app.appConfig.bellSyncMultiplier > 0) {
// the bell is delayed, need to roll the "now" time back
// subtract some time
syncedNow = Time.diff(now, app.appConfig.bellSyncDiff);
}
}
assert counterTarget != null;
if (lessons.size() == 0 || syncedNow.getValue() > counterTarget.getValue()) {
findLessons(syncedNow);
}
else {
scheduleUpdate(updateCounter(syncedNow));
}
}
private void scheduleUpdate(long newRefreshInterval) {
try {
timetableTimer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(() -> update());
}
}, newRefreshInterval);
}
catch (Exception e) {
e.printStackTrace();
}
}
private void findLessons(Time syncedNow) {
AsyncTask.execute(() -> {
Date today = Date.getToday();
lessons = app.db.lessonDao().getAllNearestNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today, syncedNow);
if (lessons != null && lessons.size() != 0) {
Date displayingDate = lessons.get(0).lessonDate;
if (displayingDate == null) {
runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
return;
}
int displayingWeekDay = displayingDate.getWeekDay();
Log.d(TAG, "Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
int notPassedIndex = -1;
int notPassedWeekDay = -1;
//int firstIndex = -1;
int lastIndex = -1;
int index = 0;
for (LessonFull lesson: lessons) {
if (notPassedIndex == -1 && !lesson.lessonPassed) {
if (lesson.lessonDate != null)
displayingDate = lesson.lessonDate;
displayingWeekDay = lesson.weekDay;
notPassedIndex = index;
notPassedWeekDay = lesson.weekDay;
}
if (lesson.weekDay == notPassedWeekDay) {
/*if (firstIndex == -1)
firstIndex = index;*/
lastIndex = index;
}
index++;
}
// for safety
/*if (firstIndex == -1)
firstIndex++;*/
if (notPassedIndex == -1)
notPassedIndex++;
if (lastIndex == -1)
lastIndex++;
Log.d(TAG, "Not passed index is "+notPassedIndex);
Log.d(TAG, "Last index is "+lastIndex);
Log.d(TAG, "New Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
Date finalDisplayingDate = displayingDate;
int finalNotPassedIndex = notPassedIndex;
int finalLastIndex = lastIndex;
runOnUiThread(() -> scheduleUpdate(updateViews(finalDisplayingDate, syncedNow, finalNotPassedIndex, finalLastIndex)));
}
else {
runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
}
});
}
private Time counterTarget = new Time(0, 0, 0);
private static final short TIME_TILL = 0;
private static final short TIME_LEFT = 1;
private short counterType = TIME_LEFT;
private long updateCounter(Time syncedNow) {
Time diff = Time.diff(counterTarget, syncedNow);
b.timeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds, "\n") : HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds, "\n"));
return updateInterval(app, diff);
}
private long updateViews(Date displayingDate, Time syncedNow, int notPassedIndex, int lastIndex) {
long newRefreshInterval = 1000*5;
if (displayingDate == null) {
return newRefreshInterval;
}
int dayDiff = Date.diffDays(displayingDate, Date.getToday());
if (displayingDate.getValue() != Date.getToday().getValue() && dayDiff == 0) {
dayDiff++;
}
LessonFull lessonFirst = lessons.get(dayDiff == 0 ? 0 : notPassedIndex);
// should never be out of range
LessonFull lessonLast = lessons.get(lastIndex);
boolean duringLessons = Time.inRange(lessonFirst.startTime, lessonLast.endTime, syncedNow) && dayDiff == 0;
if (duringLessons) {
LessonFull lessonCurrent = null;
LessonFull lessonNext = null;
if (lessons.get(notPassedIndex).lessonCurrent) {
lessonCurrent = lessons.get(notPassedIndex);
if (lessons.size() > notPassedIndex+1 && lessons.get(notPassedIndex+1).weekDay == displayingDate.getWeekDay())
lessonNext = lessons.get(notPassedIndex+1);
}
else {
lessonNext = lessons.get(notPassedIndex);
}
if (lessonCurrent != null) { // show time to the end of this lesson
b.lessonName.setText(lessonCurrent.subjectLongName);
counterType = TIME_LEFT;
counterTarget = lessonCurrent.endTime;
newRefreshInterval = updateCounter(syncedNow);
}
else if (lessonNext != null) { // it's break time, show time to the start of next lesson
b.lessonName.setText(R.string.lesson_break);
counterType = TIME_LEFT;
counterTarget = lessonNext.startTime;
newRefreshInterval = updateCounter(syncedNow);
}
else { // idk what it is now (during lessons, but not during lesson or a break)
b.lessonName.setText(R.string.card_timetable_wtf);
b.timeLeft.setText(R.string.card_timetable_wtf_report);
newRefreshInterval = 1000*60*2;
finish();
}
}
else {
if (syncedNow.getValue() < lessonFirst.startTime.getValue()) {
// before lessons
b.lessonName.setText(R.string.lesson_break);
counterType = TIME_LEFT;
counterTarget = lessonFirst.startTime;
newRefreshInterval = updateCounter(syncedNow);
}
else {
finish();
}
}
return newRefreshInterval;
}
@Override
protected void onDestroy() {
super.onDestroy();
//Log.d(TAG, "OnDestroy");
try {
timetableTimer.cancel();
timetableTimer.purge();
}
catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,568 @@
package pl.szczodrzynski.edziennik.ui.modules.home;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.InputType;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.PluralsRes;
import androidx.core.graphics.ColorUtils;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog;
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.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding;
import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding;
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_FINAL;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_PROPOSED;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER2_FINAL;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER2_PROPOSED;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_YEAR_FINAL;
import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_YEAR_PROPOSED;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class HomeFragment extends Fragment {
private static final String TAG = "HomeFragment";
private App app = null;
private MainActivity activity = null;
private FragmentHomeBinding b = 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_home, 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;
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_HOME, b.refreshLayout);
});*/
/*b.refreshLayout.setOnTouchListener((v, event) -> {
d(TAG, "event "+event);
event.setSource(0x10000000); // set a unique source
activity.swipeRefreshLayout.onTouchEvent(event);
return true;
});*/
/*b.refreshLayout.setOnDragListener((v, event) -> {
activity.swipeRefreshLayout.onDragEvent(event);
return true;
});*/
b.composeButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
b.composeButton.setOnClickListener((v -> {
startActivity(new Intent(activity, MessagesComposeActivity.class));
}));
//((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS()));
LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup insertPoint = b.cardInsertPoint;
assert layoutInflater != null;
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && app.appConfig.mobidziennikOldMessages == -1) {
new MaterialDialog.Builder(activity)
.title("Nowy moduł wiadomości")
.content("Czy chcesz używać nowego modułu wiadomości?\n\nObejmuje lepsze powiadomienia, działa szybciej, pozwala na przeglądanie pobranych wiadomości bez internetu.\n\nNowy moduł (jeszcze) nie pozwala na wysyłanie wiadomości.")
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> {
if (app.appConfig.mobidziennikOldMessages != 0) {
// need to change old to new
app.appConfig.mobidziennikOldMessages = 0;
app.saveConfig("mobidziennikOldMessages");
MainActivity.Companion.setUseOldMessages(false);
activity.loadProfile(App.profileId);
}
}))
.onNegative(((dialog, which) -> {
if (app.appConfig.mobidziennikOldMessages != 1) {
// need to change from ?? to old
app.appConfig.mobidziennikOldMessages = 1;
app.saveConfig("mobidziennikOldMessages");
MainActivity.Companion.setUseOldMessages(true);
activity.loadProfile(App.profileId);
}
}))
.show();
}
b.mobidziennikMessagesSwitch.setVisibility(app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK ? View.VISIBLE : View.GONE);
b.mobidziennikMessagesSwitch.setOnClickListener((v -> {
new MaterialDialog.Builder(activity)
.title("Nowy moduł wiadomości")
.content("Czy chcesz używać nowego modułu wiadomości?\n\nObejmuje lepsze powiadomienia, działa szybciej, pozwala na przeglądanie pobranych wiadomości bez internetu.\n\nNowy moduł (jeszcze) nie pozwala na wysyłanie wiadomości.")
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> {
if (app.appConfig.mobidziennikOldMessages != 0) {
// need to change old to new
app.appConfig.mobidziennikOldMessages = 0;
app.saveConfig("mobidziennikOldMessages");
MainActivity.Companion.setUseOldMessages(false);
activity.loadProfile(App.profileId);
}
}))
.onNegative(((dialog, which) -> {
if (app.appConfig.mobidziennikOldMessages != 1) {
// need to change from ?? to old
app.appConfig.mobidziennikOldMessages = 1;
app.saveConfig("mobidziennikOldMessages");
MainActivity.Companion.setUseOldMessages(true);
activity.loadProfile(App.profileId);
}
}))
.show();
}));
/*if (!app.profile.loggedIn) {
View cardLoginRoot = layoutInflater.inflate(R.layout.card_login, null);
CardView cardLogin = cardLoginRoot.findViewById(R.id.cardLogin);
cardLogin.setVisibility(app.profile.loggedIn ? View.GONE : View.VISIBLE);
Button cardLoginButton = cardLoginRoot.findViewById(R.id.cardLoginButton);
buttonAddDrawable(c, cardLoginButton, CommunityMaterial.Icon.cmd_arrow_right);
cardLoginButton.setOnClickListener((v1 -> {
new Handler().postDelayed(() -> {
a.runOnUiThread(() -> {
Intent i = new Intent("android.intent.action.MAIN")
.putExtra(MainActivity.ACTION_CHANGE_CURRENT_VIEW, "yes, sure")
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_SETTINGS)
.putExtra("settingsPage", SettingsFragment.PAGE_REGISTER);
app.getContext().sendBroadcast(i);
});
}, 100);
}));
insertPoint.addView(cardLoginRoot, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}*/
if (app.appConfig.updateVersion != null && !app.appConfig.updateVersion.equals("")) {
if (app.appConfig.updateVersion.equals(BuildConfig.VERSION_NAME)) {
app.appConfig.updateVersion = "";
app.saveConfig("updateVersion");
}
else {
CardUpdateBinding b;
b = DataBindingUtil.inflate(layoutInflater, R.layout.card_update, insertPoint, false);
insertPoint.addView(b.getRoot());
b.cardUpdateText.setText(getString(R.string.card_update_text_format, BuildConfig.VERSION_NAME, app.appConfig.updateVersion));
buttonAddDrawable(activity, b.cardUpdateButton, CommunityMaterial.Icon.cmd_arrow_right);
b.cardUpdateButton.setOnClickListener((v1 -> {
if (UPDATES_ON_PLAY_STORE) {
Utils.openGooglePlay(activity, "pl.szczodrzynski.edziennik");
}
else {
BootReceiver.update_url = app.appConfig.updateUrl;
BootReceiver.update_filename = app.appConfig.updateFilename;
BootReceiver.update_download_id = BootReceiver.downloadFile(app);
}
}));
}
}
if (app.profile.getLuckyNumberEnabled()
&& app.profile.getLuckyNumber() != -1
&& app.profile.getLuckyNumberDate() != null && app.profile.getLuckyNumberDate().getValue() == Date.getToday().getValue()) {
CardLuckyNumberBinding b;
b = DataBindingUtil.inflate(layoutInflater, R.layout.card_lucky_number, insertPoint, false);
insertPoint.addView(b.getRoot());
b.cardLuckyNumberTitle.setText(getString(R.string.card_lucky_number_title_format, app.profile.getLuckyNumber()));
if (app.profile.getStudentNumber() == -1) {
b.cardLuckyNumberText.setText(R.string.card_lucky_number_not_set);
}
else {
b.cardLuckyNumberText.setText(getString(R.string.card_lucky_number_text_format, app.profile.getStudentNumber()));
}
b.cardLuckyNumber.setOnClickListener(v1 -> setNumberDialog());
}
timetableCard = new HomeTimetableCard(app, activity, this, layoutInflater, insertPoint);
timetableCard.run();
configCardGrades(activity, layoutInflater, activity, insertPoint);
activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_set_student_number)
.withIcon(CommunityMaterial.Icon.cmd_counter)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
setNumberDialog();
}),
new BottomSheetSeparatorItem(true)
);
activity.gainAttention();
}
public void setNumberDialog() {
new MaterialDialog.Builder(activity)
.title(R.string.card_lucky_number_set_title)
.content(R.string.card_lucky_number_set_text)
.inputType(InputType.TYPE_CLASS_NUMBER)
.input(null, app.profile.getStudentNumber() == -1 ? "" : Utils.intToStr(app.profile.getStudentNumber()), (dialog, input) -> {
try {
app.profile.setStudentNumber(Utils.strToInt(input.toString()));
AsyncTask.execute(() -> app.profileSaveAsync());
activity.reloadTarget();
}
catch (Exception e) {
Toast.makeText(activity, R.string.incorrect_format, Toast.LENGTH_SHORT).show();
}
}).show();
}
public static String plural(Context c, @PluralsRes int resId, int value) {
return c.getResources().getQuantityString(resId, value, value);
}
public static long updateInterval(App app, Time diff) {
//Log.d(TAG, "Millis is "+System.currentTimeMillis() % 1000+", update in "+(1000-(System.currentTimeMillis() % 1000)));
if (app.appConfig.countInSeconds) {
return 1000-(System.currentTimeMillis() % 1000);
}
if (diff.minute > 5) {
//Log.d(TAG, "60 secs");
return 60000-(System.currentTimeMillis() % 60000);
}
else if (diff.minute >= 1) {
//Log.d(TAG, "3 secs");
return 3000-(System.currentTimeMillis() % 3000);
}
else if (diff.second >= 40) {
//Log.d(TAG, "2 secs");
return 2000-(System.currentTimeMillis() % 2000);
}
else {
Log.d(TAG, "1 sec");
return 1000-(System.currentTimeMillis() % 1000);
}
}
public static String timeTill(Context c, Time t1, Time t2) {
return timeTill(c, Time.diff(t1, t2));
}
public static String timeTill(Context c, Time diff) {
return timeTill(c, diff, false);
}
public static String timeTill(Context c, Time diff, boolean countInSeconds) {
return timeTill(c, diff, countInSeconds, " ");
}
public static String timeTill(Context c, Time diff, boolean countInSeconds, String textDelimiter) {
if (countInSeconds) {
int seconds = diff.second + diff.minute * 60 + diff.hour * 3600;
return plural(c, R.plurals.time_till_text, seconds)+textDelimiter+plural(c, R.plurals.time_till_seconds, seconds);
}
if (diff.hour < 1 && diff.minute < 1) {
return plural(c, R.plurals.time_till_text, diff.second)+textDelimiter+plural(c, R.plurals.time_till_seconds, diff.second);
}
else if (diff.hour < 1 && diff.minute < 5) {
return plural(c, R.plurals.time_till_text, diff.minute)+textDelimiter+plural(c, R.plurals.time_till_minutes, diff.minute)+" "+plural(c, R.plurals.time_till_seconds, diff.second);
}
else if (diff.hour < 1) {
return plural(c, R.plurals.time_till_text, diff.minute)+textDelimiter+plural(c, R.plurals.time_till_minutes, diff.minute);
}
else { // diff.hour > 1
return plural(c, R.plurals.time_till_text, diff.hour)+textDelimiter+plural(c, R.plurals.time_till_hours, diff.hour)+" "+plural(c, R.plurals.time_till_minutes, diff.minute);
}
}
public static String timeLeft(Context c, Time diff) {
return timeLeft(c, diff, false);
}
public static String timeLeft(Context c, Time diff, boolean countInSeconds) {
return timeLeft(c, diff, countInSeconds, " ");
}
public static String timeLeft(Context c, Time diff, boolean countInSeconds, String textDelimiter) {
if (countInSeconds) {
int seconds = diff.second + diff.minute * 60 + diff.hour * 3600;
return plural(c, R.plurals.time_left_text, seconds)+textDelimiter+plural(c, R.plurals.time_left_seconds, seconds);
}
if (diff.hour < 1 && diff.minute < 1) {
return plural(c, R.plurals.time_left_text, diff.second)+textDelimiter+plural(c, R.plurals.time_left_seconds, diff.second);
}
else if (diff.hour < 1 && diff.minute < 5) {
return plural(c, R.plurals.time_left_text, diff.minute)+textDelimiter+plural(c, R.plurals.time_left_minutes, diff.minute)+" "+plural(c, R.plurals.time_left_seconds, diff.second);
}
else if (diff.hour < 1) {
return plural(c, R.plurals.time_left_text, diff.minute)+textDelimiter+plural(c, R.plurals.time_left_minutes, diff.minute);
}
else { // diff.hour > 1
return plural(c, R.plurals.time_left_text, diff.hour)+textDelimiter+plural(c, R.plurals.time_left_hours, diff.hour)+" "+plural(c, R.plurals.time_left_minutes, diff.minute);
}
}
public static void buttonAddDrawable(Context c, Button button, CommunityMaterial.Icon icon)
{
button.setCompoundDrawables(null, null, new IconicsDrawable(c)
.icon(icon)
.color(IconicsColor.colorInt(Utils.getAttr(c, android.R.attr.textColorPrimary)))
.size(IconicsSize.dp(16)), null);
}
public static Date findDateWithLessons(int profileId, List<LessonFull> lessons) {
return findDateWithLessons(profileId, lessons, 0);
}
public static Date findDateWithLessons(int profileId, List<LessonFull> lessons, int nextDayHourThreshold) {
return findDateWithLessons(profileId, lessons, Time.getNow(), nextDayHourThreshold);
}
public static Date findDateWithLessons(int profileId, List<LessonFull> lessons, Time now) {
return findDateWithLessons(profileId, lessons, now, 0);
}
public static Date findDateWithLessons(int profileId, @NonNull List<LessonFull> lessons, Time now, int nextDayHourThreshold) {
now = now.clone().stepForward(-nextDayHourThreshold, 0, 0);
Date displayingDate = Date.getToday();
int displayingWeekDay = displayingDate.getWeekDay();
//boolean foundSomething = false;
//int weekDayNum = displayingDate.getWeekDay();//Week.getTodayWeekDay();
//int checkedDays = 0;
for (LessonFull lesson: lessons) {
if (lesson.profileId != profileId)
continue;
if (lesson.weekDay == displayingWeekDay
&& now.getValue() <= lesson.endTime.getValue()) {
return lesson.lessonDate;
}
if (lesson.weekDay != displayingWeekDay) {
return lesson.lessonDate;
}
}
return displayingDate;
/*while (!foundSomething && checkedDays < 14)
{
weekDay = profile.timetable.weekdays[displayingDate.getWeekDay()];
if (weekDay.lessons.size() == 0) // this day has no lessons
{
displayingDate.stepForward(0, 0, 1);
checkedDays++;
continue;
}
if (displayingDate.getWeekDay() == Week.getTodayWeekDay() // today
&& now.getValue() > weekDay.lessons.get(weekDay.lessons.size() - 1).endTime.getValue()) // this day has lessons, but last lesson is over already
{
displayingDate.stepForward(0, 0, 1);
checkedDays++;
continue;
}
// this day has lessons, and we are during the lessons
foundSomething = true;
}
return displayingDate;*/
}
private void updateCardGrades(Context c, Activity a, View root, int maxWidthPx) {
if (a == null || !isAdded())
return;
TextView cardGradesNoData = root.findViewById(R.id.cardGradesNoData);
app.db.gradeDao().getAllWhere(App.profileId, "gradeSemester = "+ app.profile.getCurrentSemester() +" AND addedDate > "+(System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000)).observe(this, grades -> {
List<ItemGradesSubjectModel> subjectList = new ArrayList<>();
// now we have all grades from the newest to the oldest
for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) {
subjectList.add(new ItemGradesSubjectModel(app.profile, new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new ArrayList<>(), new ArrayList<>()));
model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
}
if (model != null) { // should always be not null
model.grades1.add(grade);
}
}
float scale = c.getResources().getDisplayMetrics().density;
int _5dp = (int) (5 * scale + 0.5f);
int _8dp = (int) (8 * scale + 0.5f);
LinearLayout cardGradesList = root.findViewById(R.id.cardGradesList);
cardGradesList.removeAllViews();
LinearLayout.LayoutParams textLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
textLayoutParams.setMargins(0, 0, _5dp, 0);
LinearLayout.LayoutParams linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
linearLayoutParams.setMargins(_8dp, 0, _8dp, _5dp);
//Log.d(TAG, "maxWidthPx "+maxWidthPx);
int count = 0;
if (subjectList.size() > 0) {
for (ItemGradesSubjectModel subjectModel : subjectList) {
if (count++ >= 8) {
//continue;
}
LinearLayout gradeItem = new LinearLayout(cardGradesList.getContext());
gradeItem.setOrientation(LinearLayout.HORIZONTAL);
int totalWidthPx = 0;//subjectName.getMeasuredWidth() + _5dp;
boolean ellipsized = false;
for (GradeFull grade : subjectModel.grades1) {
if (ellipsized)
continue;
int gradeColor;
if (app.profile.getGradeColorMode() == Profile.COLOR_MODE_DEFAULT) {
gradeColor = grade.color;
}
else {
gradeColor = Colors.gradeToColor(grade);
}
TextView gradeName = new TextView(gradeItem.getContext());
gradeName.setText(grade.name);
if (grade.type == TYPE_SEMESTER1_PROPOSED
|| grade.type == TYPE_SEMESTER2_PROPOSED) {
gradeName.setText(getString(R.string.grade_semester_proposed_format, grade.name));
}
else if (grade.type == TYPE_SEMESTER1_FINAL
|| grade.type == TYPE_SEMESTER2_FINAL) {
gradeName.setText(getString(R.string.grade_semester_final_format, grade.name));
}
else if (grade.type == TYPE_YEAR_PROPOSED) {
gradeName.setText(getString(R.string.grade_year_proposed_format, grade.name));
}
else if (grade.type == TYPE_YEAR_FINAL) {
gradeName.setText(getString(R.string.grade_year_final_format, grade.name));
}
gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
gradeName.setTypeface(null, Typeface.BOLD);
gradeName.setBackgroundResource(R.drawable.bg_rounded_4dp);
gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
gradeName.setPadding(_5dp, 0, _5dp, 0);
gradeName.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
totalWidthPx += gradeName.getMeasuredWidth() + _5dp;
//Log.d(TAG, "totalWidthPx "+totalWidthPx);
if (totalWidthPx >= (float)maxWidthPx / 1.5f) {
ellipsized = true;
TextView ellipsisText = new TextView(gradeItem.getContext());
ellipsisText.setText(R.string.ellipsis);
ellipsisText.setTextAppearance(gradeItem.getContext(), R.style.NavView_TextView);
ellipsisText.setTypeface(null, Typeface.BOLD);
ellipsisText.setPadding(0, 0, 0, 0);
gradeItem.addView(ellipsisText, textLayoutParams);
}
else {
gradeItem.addView(gradeName, textLayoutParams);
}
}
TextView subjectName = new TextView(gradeItem.getContext());
Subject subject = subjectModel.subject;
subjectName.setText(" z "+(subject != null ? subject.longName : ""));
subjectName.setEllipsize(TextUtils.TruncateAt.END);
subjectName.setSingleLine();
subjectName.measure(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
//Log.d(TAG, "subjectName.width "+totalWidthPx);
//subjectName.setMaxWidth(maxWidthPx - totalWidthPx);
gradeItem.addView(subjectName, textLayoutParams);
cardGradesList.addView(gradeItem, linearLayoutParams);
}
}
else {
cardGradesNoData.setVisibility(View.VISIBLE);
cardGradesList.setVisibility(View.GONE);
}
});
Button cardGradesButton = root.findViewById(R.id.cardGradesButton);
buttonAddDrawable(c, cardGradesButton, CommunityMaterial.Icon.cmd_arrow_right);
cardGradesButton.setOnClickListener((v1 -> new Handler().postDelayed(() -> a.runOnUiThread(() -> {
activity.loadTarget(MainActivity.DRAWER_ITEM_GRADES, null);
}), 100)));
//new Handler().postDelayed(() -> a.runOnUiThread(() -> updateCardGrades(c, a, root)), newRefreshInterval);
}
@Override
public void onDestroy() {
super.onDestroy();
if (timetableCard != null)
timetableCard.destroy();
}
private void configCardGrades(Context c, LayoutInflater layoutInflater, Activity a, ViewGroup insertPoint) {
View root = layoutInflater.inflate(R.layout.card_grades, null);
DisplayMetrics displayMetrics = c.getResources().getDisplayMetrics();
updateCardGrades(c, a, root, displayMetrics.widthPixels - Utils.dpToPx((app.appConfig.miniDrawerVisible ? 72 : 0)/*miniDrawer size*/ + 24 + 24/*left and right offsets*/ + 16/*ellipsize width*/));
insertPoint.addView(root, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
private HomeTimetableCard timetableCard;
}

View File

@ -0,0 +1,473 @@
package pl.szczodrzynski.edziennik.ui.modules.home;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.databinding.DataBindingUtil;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.CardTimetableBinding;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.models.Week;
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CANCELLED;
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CHANGE;
import static pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment.updateInterval;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
public class HomeTimetableCard {
private static final String TAG = "HomeTimetableCard";
private App app;
private MainActivity a;
private HomeFragment f;
private LayoutInflater layoutInflater;
private ViewGroup insertPoint;
private CardTimetableBinding b;
private Timer timetableTimer;
private Time bellSyncTime = null;
public HomeTimetableCard(App app, MainActivity a, HomeFragment f, LayoutInflater layoutInflater, ViewGroup insertPoint) {
this.app = app;
this.a = a;
this.f = f;
this.layoutInflater = layoutInflater;
this.insertPoint = insertPoint;
}
public void run() {
timetableTimer = new Timer();
b = DataBindingUtil.inflate(layoutInflater, R.layout.card_timetable, null, false);
update();
insertPoint.addView(b.getRoot(), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
b.cardTimetableFullscreenCounter.setOnClickListener((v -> {
Intent intent = new Intent(a, CounterActivity.class);
a.startActivity(intent);
}));
b.cardTimetableBellSync.setOnClickListener(v -> {
if (bellSyncTime == null) {
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(R.string.bell_sync_cannot_now)
.positiveText(R.string.ok)
.show();
}
else {
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(app.getString(R.string.bell_sync_howto, bellSyncTime.getStringHM())+
(app.appConfig.bellSyncDiff != null ?
""+app.getString(R.string.bell_sync_current_dialog, (app.appConfig.bellSyncMultiplier == -1 ? "-" : "+")+app.appConfig.bellSyncDiff.getStringHMS())
: ""))
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.neutralText(R.string.reset)
.onPositive((dialog, which) -> {
Time bellDiff = Time.diff(Time.getNow(), bellSyncTime);
app.appConfig.bellSyncDiff = bellDiff;
app.appConfig.bellSyncMultiplier = (bellSyncTime.getValue() > Time.getNow().getValue() ? -1 : 1);
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(app.getString(R.string.bell_sync_results, (bellSyncTime.getValue() > Time.getNow().getValue() ? "-" : "+"), bellDiff.getStringHMS()))
.positiveText(R.string.ok)
.show();
})
.onNeutral((dialog, which) -> {
new MaterialDialog.Builder(a)
.title(R.string.bell_sync_title)
.content(R.string.bell_sync_reset_confirm)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog1, which1) -> {
app.appConfig.bellSyncDiff = null;
app.appConfig.bellSyncMultiplier = 0;
app.saveConfig("bellSyncDiff", "bellSyncMultiplier");
}))
.show();
})
.show();
}
});
HomeFragment.buttonAddDrawable(a, b.cardTimetableButton, CommunityMaterial.Icon.cmd_arrow_right);
}
private List<LessonFull> lessons = new ArrayList<>();
private List<EventFull> events = new ArrayList<>();
private void update() {
//Log.d(TAG, "Now "+System.currentTimeMillis());
if (a == null || !f.isAdded())
return;
// BELL SYNCING
Time now = Time.getNow();
Time syncedNow = now;
//Time updateDiff = null;
if (app.appConfig.bellSyncDiff != null) {
if (app.appConfig.bellSyncMultiplier < 0) {
// the bell is too fast, need to step further to go with it
// add some time
syncedNow = Time.sum(now, app.appConfig.bellSyncDiff);
//Toast.makeText(c, "Bell sync diff is "+app.appConfig.bellSyncDiff.getStringHMS()+"\n\n Synced now is "+syncedNow.getStringHMS(), Toast.LENGTH_LONG).show();
}
if (app.appConfig.bellSyncMultiplier > 0) {
// the bell is delayed, need to roll the "now" time back
// subtract some time
syncedNow = Time.diff(now, app.appConfig.bellSyncDiff);
}
}
assert counterTarget != null;
if (lessons.size() == 0 || syncedNow.getValue() > counterTarget.getValue()) {
findLessons(syncedNow);
}
else {
scheduleUpdate(updateCounter(syncedNow));
}
}
private void scheduleUpdate(long newRefreshInterval) {
try {
timetableTimer.schedule(new TimerTask() {
@Override
public void run() {
a.runOnUiThread(() -> update());
}
}, newRefreshInterval);
}
catch (Exception e) {
e.printStackTrace();
}
}
private void findLessons(Time syncedNow) {
AsyncTask.execute(() -> {
Date today = Date.getToday();
lessons = app.db.lessonDao().getAllNearestNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today, syncedNow);
if (lessons != null && lessons.size() != 0) {
Date displayingDate = lessons.get(0).lessonDate;
if (displayingDate == null) {
a.runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
return;
}
int displayingWeekDay = displayingDate.getWeekDay();
Log.d(TAG, "Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
int notPassedIndex = -1;
int notPassedWeekDay = -1;
//int firstIndex = -1;
int lastIndex = -1;
int index = 0;
for (LessonFull lesson: lessons) {
if (notPassedIndex == -1 && !lesson.lessonPassed) {
if (lesson.lessonDate != null)
displayingDate = lesson.lessonDate;
displayingWeekDay = lesson.weekDay;
notPassedIndex = index;
notPassedWeekDay = lesson.weekDay;
}
if (lesson.weekDay == notPassedWeekDay) {
/*if (firstIndex == -1)
firstIndex = index;*/
lastIndex = index;
}
index++;
}
// for safety
/*if (firstIndex == -1)
firstIndex++;*/
if (notPassedIndex == -1)
notPassedIndex++;
if (lastIndex == -1)
lastIndex++;
Log.d(TAG, "Not passed index is "+notPassedIndex);
Log.d(TAG, "Last index is "+lastIndex);
Log.d(TAG, "New Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
events = app.db.eventDao().getAllByDateNow(App.profileId, displayingDate);
Date finalDisplayingDate = displayingDate;
int finalNotPassedIndex = notPassedIndex;
int finalLastIndex = lastIndex;
a.runOnUiThread(() -> scheduleUpdate(updateViews(finalDisplayingDate, syncedNow, finalNotPassedIndex, finalLastIndex)));
}
else {
a.runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
}
});
}
private Time counterTarget = new Time(0, 0, 0);
private static final short TIME_TILL = 0;
private static final short TIME_LEFT = 1;
private short counterType = TIME_TILL;
private long updateCounter(Time syncedNow) {
Time diff = Time.diff(counterTarget, syncedNow);
b.cardTimetableTimeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds) : HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds));
bellSyncTime = counterTarget;
b.cardTimetableFullscreenCounter.setVisibility(View.VISIBLE);
return updateInterval(app, diff);
}
private long updateViews(Date displayingDate, Time syncedNow, int notPassedIndex, int lastIndex) {
//Date weekEnd = Date.getToday().stepForward(0, 0, 6 - Week.getTodayWeekDay()); // Sunday of the current week
if (displayingDate == null) {
b.cardTimetableTitle.setText(R.string.card_timetable_no_timetable);
b.cardTimetableNoData.setVisibility(View.VISIBLE);
b.cardTimetableContent.setVisibility(View.GONE);
return 1000*60*30;
}
// at this point lessons.size() must be greater than 0
int displayingWeekDay = displayingDate.getWeekDay();
String weekDayStr = Week.getFullDayName(displayingWeekDay);
int dayDiff = Date.diffDays(displayingDate, Date.getToday());
if (displayingDate.getValue() != Date.getToday().getValue() && dayDiff == 0) {
dayDiff++;
}
String dayDiffStr = (dayDiff == 0 ? app.getString(R.string.day_today_format, weekDayStr) : (dayDiff == 1 ? app.getString(R.string.day_tomorrow_format,weekDayStr) : app.getString(R.string.day_other_format, weekDayStr, displayingDate.getStringDm())));
b.cardTimetableTitle.setText(app.getString(R.string.card_timetable_title, dayDiffStr));
long newRefreshInterval = 1000*5; // 5 seconds
b.cardTimetableNoData.setVisibility(View.GONE);
b.cardTimetableContent.setVisibility(View.VISIBLE);
LessonFull lessonFirst = lessons.get(dayDiff == 0 ? 0 : notPassedIndex);
// should never be out of range
LessonFull lessonLast = lessons.get(lastIndex);
boolean duringLessons = Time.inRange(lessonFirst.startTime, lessonLast.endTime, syncedNow) && dayDiff == 0;
if (!duringLessons) {
LessonFull lessonSecond = null;
if (lessons.size() > notPassedIndex+1 && lessons.get(notPassedIndex+1).weekDay == displayingDate.getWeekDay())
lessonSecond = lessons.get(notPassedIndex+1);
b.cardTimetableType.setText(R.string.card_timetable_lesson_duration);
b.cardTimetableSummary.setText(app.getString(R.string.card_timetable_lesson_duration_format, lessonFirst.startTime.getStringHM(), lessonLast.endTime.getStringHM()));
if (dayDiff == 0) {
counterTarget = lessonFirst.startTime;
counterType = TIME_TILL;
newRefreshInterval = updateCounter(syncedNow);
}
else {
b.cardTimetableTimeLeft.setText("");
newRefreshInterval = 1000*60*30;
b.cardTimetableFullscreenCounter.setVisibility(View.GONE);
}
String lessonFirstStr = null;
if (lessonFirst.changeId != 0) {
if (lessonFirst.changeType == TYPE_CANCELLED) {
lessonFirstStr = "<del>" + bs(lessonFirst.getSubjectLongName()) + "</del>";
} else if (lessonFirst.changeType == TYPE_CHANGE) {
lessonFirstStr = "<i>" + bs(lessonFirst.getSubjectLongName()) + "</i>";
}
} else {
lessonFirstStr = bs(lessonFirst.subjectLongName);
}
String lessonSecondStr = null;
if (lessonSecond != null) {
if (lessonSecond.changeId != 0) {
if (lessonSecond.changeType == TYPE_CANCELLED) {
lessonSecondStr = "<del>" + bs(lessonSecond.getSubjectLongName()) + "</del>";
} else if (lessonSecond.changeType == TYPE_CHANGE) {
lessonSecondStr = "<i>" + bs(lessonSecond.getSubjectLongName()) + "</i>";
}
} else {
lessonSecondStr = bs(lessonSecond.getSubjectLongName());
}
}
b.cardTimetableLessonOverview.setText(
Html.fromHtml(
app.getString(R.string.card_timetable_lesson_overview,
lessonFirst.startTime.getStringHM(),
bs(null, lessonFirstStr, ", ")+lessonFirst.getClassroomName(),
(lessonSecond == null ? "" : lessonSecond.startTime.getStringHM()),
(lessonSecond == null ? "" : bs(null, lessonSecondStr, ", "+lessonSecond.getClassroomName())))
)
);
StringBuilder eventSummary = new StringBuilder();
eventSummary.append(app.getString(R.string.card_timetable_event_overview));
for (EventFull event : events) {
if (displayingDate.getValue() == event.eventDate.getValue()) {
eventSummary.append(app.getString(
R.string.card_timetable_event_overview_format,
event.startTime == null ? app.getString(R.string.event_all_day) : event.startTime.getStringHM(),
event.typeName,
event.topic));
}
}
b.cardTimetableEventOverview.setText(eventSummary.toString());
}
else {
b.cardTimetableFullscreenCounter.setVisibility(View.VISIBLE);
LessonFull lessonCurrent = null;
LessonFull lessonInAMoment = null;
LessonFull lessonNext = null;
LessonFull lessonAfterNext = null;
if (lessons.get(notPassedIndex).lessonCurrent) {
lessonCurrent = lessons.get(notPassedIndex);
if (lessons.size() > notPassedIndex+1 && lessons.get(notPassedIndex+1).weekDay == displayingDate.getWeekDay())
lessonNext = lessons.get(notPassedIndex+1);
if (lessons.size() > notPassedIndex+2 && lessons.get(notPassedIndex+2).weekDay == displayingDate.getWeekDay())
lessonAfterNext = lessons.get(notPassedIndex+2);
}
else {
lessonInAMoment = lessons.get(notPassedIndex);
if (lessons.size() > notPassedIndex+1 && lessons.get(notPassedIndex+1).weekDay == displayingDate.getWeekDay())
lessonNext = lessons.get(notPassedIndex+1);
if (lessons.size() > notPassedIndex+2 && lessons.get(notPassedIndex+2).weekDay == displayingDate.getWeekDay())
lessonAfterNext = lessons.get(notPassedIndex+2);
}
if (lessonCurrent != null) { // show time to the end of this lesson
b.cardTimetableType.setText(R.string.card_timetable_now);
if (lessonCurrent.changeId != 0) {
if (lessonCurrent.changeType == TYPE_CANCELLED) {
b.cardTimetableSummary.setText(Html.fromHtml("<del>"+bs(lessonCurrent.getSubjectLongName())+"</del><br>"+lessonCurrent.getClassroomName()));
}
else if (lessonCurrent.changeType == TYPE_CHANGE) {
b.cardTimetableSummary.setText(Html.fromHtml("<i>"+bs(lessonCurrent.getSubjectLongName())+"<br>"+lessonCurrent.getClassroomName()+"</i>"));
}
}
else {
b.cardTimetableSummary.setText(bs(lessonCurrent.getSubjectLongName())+"\n"+lessonCurrent.getClassroomName());
}
counterTarget = lessonCurrent.endTime;
counterType = TIME_LEFT;
newRefreshInterval = updateCounter(syncedNow);
}
else if (lessonInAMoment != null) { // it's break time, show time to the start of next lesson
b.cardTimetableType.setText(R.string.card_timetable_following);
if (lessonInAMoment.changeId != 0) {
if (lessonInAMoment.changeType == TYPE_CANCELLED) {
b.cardTimetableSummary.setText(Html.fromHtml("<del>"+bs(lessonInAMoment.getSubjectLongName())+"</del><br>"+lessonInAMoment.getClassroomName()));
}
else if (lessonInAMoment.changeType == TYPE_CHANGE) {
b.cardTimetableSummary.setText(Html.fromHtml("<i>"+bs(lessonInAMoment.getSubjectLongName())+"<br>"+lessonInAMoment.getClassroomName()+"</i>"));
}
}
else {
b.cardTimetableSummary.setText(bs(lessonInAMoment.getSubjectLongName())+"\n"+lessonInAMoment.getClassroomName());
}
counterTarget = lessonInAMoment.startTime;
counterType = TIME_TILL;
newRefreshInterval = updateCounter(syncedNow);
}
else { // idk what it is now (during lessons, but not during lesson or a break)
b.cardTimetableType.setText(R.string.card_timetable_wtf);
b.cardTimetableSummary.setText(R.string.card_timetable_wtf_report);
b.cardTimetableTimeLeft.setText("");
newRefreshInterval = 1000*60*2;
}
String lessonNextStr = null;
if (lessonNext != null) {
if (lessonNext.changeId != 0) {
if (lessonNext.changeType == TYPE_CANCELLED) {
lessonNextStr = "<del>" + lessonNext.getSubjectLongName() + "</del>";
} else if (lessonNext.changeType == TYPE_CHANGE) {
lessonNextStr = "<i>" + lessonNext.getSubjectLongName() + "</i>";
}
} else {
lessonNextStr = lessonNext.getSubjectLongName();
}
}
String lessonAfterNextStr = null;
if (lessonAfterNext != null) {
if (lessonAfterNext.changeId != 0) {
if (lessonAfterNext.changeType == TYPE_CANCELLED) {
lessonAfterNextStr = "<del>" + lessonAfterNext.getSubjectLongName() + "</del>";
} else if (lessonAfterNext.changeType == TYPE_CHANGE) {
lessonAfterNextStr = "<i>" + lessonAfterNext.getSubjectLongName() + "</i>";
}
} else {
lessonAfterNextStr = lessonAfterNext.getSubjectLongName();
}
}
b.cardTimetableLessonOverview.setText(
Html.fromHtml(app.getString(
R.string.card_timetable_lesson_overview_ongoing,
(lessonNext == null ? "" : lessonNext.startTime.getStringHM()),
(lessonNext == null ? "" : bs(null, lessonNextStr, ", "+lessonNext.getClassroomName())),
(lessonAfterNext == null ? "" : lessonAfterNext.startTime.getStringHM()),
(lessonAfterNext == null ? "" : bs(null, lessonAfterNextStr, ", "+lessonAfterNext.getClassroomName()))
))
);
StringBuilder eventSummary = new StringBuilder();
eventSummary.append(app.getString(R.string.card_timetable_event_overview));
for (EventFull event : events) {
// display the event only if it's AllDay or it hasn't started yet
boolean timeMatching = event.startTime == null || syncedNow.getValue() < event.startTime.getValue();
if (displayingDate.getValue() == event.eventDate.getValue()
&& timeMatching) {
eventSummary.append(app.getString(
R.string.card_timetable_event_overview_format,
event.startTime == null ? app.getString(R.string.agenda_event_all_day)+" - " : event.startTime.getStringHM(),
event.typeName,
event.topic));
}
}
b.cardTimetableEventOverview.setText(eventSummary.toString());
}
b.cardTimetableButton.setOnClickListener((v1 -> new Handler().postDelayed(() -> a.runOnUiThread(() -> {
Bundle arguments = new Bundle();
arguments.putInt("timetableDate", displayingDate.getValue());
a.loadTarget(MainActivity.DRAWER_ITEM_TIMETABLE, arguments);
}), 100)));
return newRefreshInterval;
//new Handler().postDelayed(() -> a.runOnUiThread(() -> updateCardTimetable(c, a, root)), newRefreshInterval);
}
void destroy() {
//Log.d(TAG, "OnDestroy");
try {
timetableTimer.cancel();
timetableTimer.purge();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,132 @@
package pl.szczodrzynski.edziennik.ui.modules.homework;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
public class HomeworkAdapter extends RecyclerView.Adapter<HomeworkAdapter.ViewHolder> {
private Context context;
private List<EventFull> homeworkList;
//getting the context and product list with constructor
public HomeworkAdapter(Context mCtx, List<EventFull> homeworkList) {
this.context = mCtx;
this.homeworkList = homeworkList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_homework_item, parent, false);
return new ViewHolder(view);
}
public static String dayDiffString(Context context, int dayDiff) {
if (dayDiff > 0) {
if (dayDiff == 1) {
return context.getString(R.string.tomorrow);
}
else if (dayDiff == 2) {
return context.getString(R.string.the_day_after);
}
return HomeFragment.plural(context, R.plurals.time_till_days, Math.abs(dayDiff));
}
else if (dayDiff < 0) {
if (dayDiff == -1) {
return context.getString(R.string.yesterday);
}
else if (dayDiff == -2) {
return context.getString(R.string.the_day_before);
}
return context.getString(R.string.ago_format, HomeFragment.plural(context, R.plurals.time_till_days, Math.abs(dayDiff)));
}
return context.getString(R.string.today);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
EventFull homework = homeworkList.get(position);
int diffDays = Date.diffDays(homework.eventDate, Date.getToday());
holder.homeworkItemHomeworkDate.setText(app.getString(R.string.date_relative_format, homework.eventDate.getFormattedString(), dayDiffString(context, diffDays)));
holder.homeworkItemTopic.setText(homework.topic);
holder.homeworkItemSubjectTeacher.setText(context.getString(R.string.homework_subject_teacher_format, bs(homework.subjectLongName), bs(homework.teacherFullName)));
holder.homeworkItemTeamDate.setText(context.getString(R.string.homework_team_date_format, bs(homework.teamName), Date.fromMillis(homework.addedDate).getFormattedStringShort()));
if (!homework.seen) {
holder.homeworkItemTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
holder.homeworkItemTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
homework.seen = true;
AsyncTask.execute(() -> {
app.db.metadataDao().setSeen(App.profileId, homework, true);
});
}
else {
holder.homeworkItemTopic.setBackground(null);
}
holder.homeworkItemEdit.setVisibility((homework.addedManually ? View.VISIBLE : View.GONE));
holder.homeworkItemEdit.setOnClickListener(v -> {
new EventManualDialog(context).show(app, homework, null, null, EventManualDialog.DIALOG_HOMEWORK);
});
if (homework.sharedBy == null) {
holder.homeworkItemSharedBy.setVisibility(View.GONE);
}
else if (homework.sharedByName != null) {
holder.homeworkItemSharedBy.setText(app.getString(R.string.event_shared_by_format, (homework.sharedBy.equals("self") ? app.getString(R.string.event_shared_by_self) : homework.sharedByName)));
}
}
@Override
public int getItemCount() {
return homeworkList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView homeworkItemCard;
TextView homeworkItemTopic;
TextView homeworkItemHomeworkDate;
TextView homeworkItemSharedBy;
TextView homeworkItemSubjectTeacher;
TextView homeworkItemTeamDate;
Button homeworkItemEdit;
ViewHolder(View itemView) {
super(itemView);
homeworkItemCard = itemView.findViewById(R.id.homeworkItemCard);
homeworkItemTopic = itemView.findViewById(R.id.homeworkItemTopic);
homeworkItemHomeworkDate = itemView.findViewById(R.id.homeworkItemHomeworkDate);
homeworkItemSharedBy = itemView.findViewById(R.id.homeworkItemSharedBy);
homeworkItemSubjectTeacher = itemView.findViewById(R.id.homeworkItemSubjectTeacher);
homeworkItemTeamDate = itemView.findViewById(R.id.homeworkItemTeamDate);
homeworkItemEdit = itemView.findViewById(R.id.homeworkItemEdit);
}
}
}

View File

@ -0,0 +1,8 @@
package pl.szczodrzynski.edziennik.ui.modules.homework
class HomeworkDate {
companion object {
const val CURRENT = 0
const val PAST = 1
}
}

View File

@ -0,0 +1,106 @@
package pl.szczodrzynski.edziennik.ui.modules.homework
import android.os.AsyncTask
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.FragmentHomeworkBinding
import pl.szczodrzynski.edziennik.datamodels.Metadata
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
class HomeworkFragment : Fragment() {
companion object {
var pageSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentHomeworkBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentHomeworkBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
activity.bottomSheet.prependItems(
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK)
}),
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
.withOnClickListener(View.OnClickListener {
activity.bottomSheet.close()
AsyncTask.execute { app.db.metadataDao().setAllSeen(App.profileId, Metadata.TYPE_HOMEWORK, true) }
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
}))
b.viewPager.adapter = MessagesFragment.Adapter(childFragmentManager).also { adapter ->
adapter.addFragment(HomeworkListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("homeworkDate", HomeworkDate.CURRENT)
}
}, getString(R.string.homework_tab_current))
adapter.addFragment(HomeworkListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("homeworkDate", HomeworkDate.PAST)
}
}, getString(R.string.homework_tab_past))
}
b.viewPager.currentItem = pageSelection
b.viewPager.clearOnPageChangeListeners()
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
pageSelection = position
}
})
b.tabLayout.setupWithViewPager(b.viewPager)
activity.navView.bottomBar.fabEnable = true
activity.navView.bottomBar.fabExtendedText = getString(R.string.add)
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon2.cmd_plus
activity.navView.setFabOnClickListener(View.OnClickListener {
EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK)
})
activity.gainAttention()
activity.collapseFab()
}
}

View File

@ -0,0 +1,77 @@
package pl.szczodrzynski.edziennik.ui.modules.homework
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.HomeworkListBinding
import pl.szczodrzynski.edziennik.datamodels.Event
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.Themes
class HomeworkListFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: HomeworkListBinding
private var homeworkDate = HomeworkDate.CURRENT
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = HomeworkListBinding.inflate(inflater)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
if (arguments != null) {
homeworkDate = arguments.getInt("homeworkDate", HomeworkDate.CURRENT)
}
val layoutManager = LinearLayoutManager(context)
layoutManager.reverseLayout = false
layoutManager.stackFromEnd = false
b.homeworkView.setHasFixedSize(true)
b.homeworkView.layoutManager = layoutManager
val filter = when(homeworkDate) {
HomeworkDate.CURRENT -> "eventDate > '" + Date.getToday().stringY_m_d + "'"
else -> "eventDate <= '" + Date.getToday().stringY_m_d + "'"
}
app.db.eventDao()
.getAllByType(App.profileId, Event.TYPE_HOMEWORK, filter)
.observe(this, Observer { homeworkList ->
if (app.profile == null || !isAdded) return@Observer
if (homeworkList != null && homeworkList.size > 0) {
val adapter = HomeworkAdapter(context, homeworkList)
b.homeworkView.adapter = adapter
b.homeworkView.visibility = View.VISIBLE
b.homeworkNoData.visibility = View.GONE
} else {
b.homeworkView.visibility = View.GONE
b.homeworkNoData.visibility = View.VISIBLE
}
})
}
}

View File

@ -0,0 +1,84 @@
package pl.szczodrzynski.edziennik.ui.modules.intro;
import android.os.Build;
import android.os.Bundle;
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
public class ChangelogIntroActivity extends IntroActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
App app = (App)getApplication();
overridePendingTransition(R.anim.fade_in, 0);
super.onCreate(savedInstanceState);
setButtonBackVisible(true);
setButtonBackFunction(BUTTON_BACK_FUNCTION_BACK);
setButtonNextVisible(true);
setButtonNextFunction(BUTTON_NEXT_FUNCTION_NEXT_FINISH);
setButtonCtaVisible(false);
setPageScrollDuration(500);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setPageScrollInterpolator(android.R.interpolator.fast_out_slow_in);
}
/*if (app.appConfig.lastAppVersion < 120) {
addSlide(new SimpleSlide.Builder()
.title(R.string.intro_web_push_title)
.description(R.string.intro_web_push_text)
.image(R.drawable.intro_web_push)
.background(R.color.introPage5Color)
.backgroundDark(R.color.introPage5ColorDark)
.buttonCtaLabel(R.string.ok)
.buttonCtaClickListener(v -> nextSlide())
.scrollable(true)
.build());
}
if (app.appConfig.lastAppVersion < 141) {
addSlide(new SimpleSlide.Builder()
.title(R.string.intro_grades_editor_title)
.description(R.string.intro_grades_editor_text)
.image(R.drawable.intro_grades_editor)
.background(R.color.introPage5Color)
.backgroundDark(R.color.introPage5ColorDark)
.buttonCtaLabel(R.string.ok)
.buttonCtaClickListener(v -> nextSlide())
.scrollable(true)
.build());
}
if (app.appConfig.lastAppVersion < 150) {
addSlide(new SimpleSlide.Builder()
.title(R.string.intro_manual_events_title)
.description(R.string.intro_manual_events_text)
.image(R.drawable.intro_manual_events)
.background(R.color.introPage5Color)
.backgroundDark(R.color.introPage5ColorDark)
.buttonCtaLabel(R.string.ok)
.buttonCtaClickListener(v -> nextSlide())
.scrollable(true)
.build());
}
if (app.appConfig.lastAppVersion < 170) {
addSlide(new SimpleSlide.Builder()
.title(R.string.intro_synergia_title)
.description(R.string.intro_synergia_text)
.image(R.drawable.intro_synergia)
.background(R.color.introPage5Color)
.backgroundDark(R.color.introPage5ColorDark)
.buttonCtaLabel(R.string.ok)
.buttonCtaClickListener(v -> nextSlide())
.scrollable(true)
.build());
}*/
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE;
app.appConfig.savePending = true;
}
}

View File

@ -0,0 +1,287 @@
/*
* MIT License
*
* Copyright (c) 2017 Jan Heinrich Reimer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package pl.szczodrzynski.edziennik.ui.modules.intro;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.ColorRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import androidx.fragment.app.Fragment;
import androidx.appcompat.view.ContextThemeWrapper;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.heinrichreimersoftware.materialintro.app.ButtonCtaFragment;
import com.heinrichreimersoftware.materialintro.app.SlideFragment;
import com.heinrichreimersoftware.materialintro.slide.ButtonCtaSlide;
import com.heinrichreimersoftware.materialintro.slide.RestorableSlide;
import com.heinrichreimersoftware.materialintro.slide.Slide;
import com.heinrichreimersoftware.materialintro.view.parallax.ParallaxFragment;
public class FragmentSlideMod implements Slide, RestorableSlide, ButtonCtaSlide {
private Fragment fragment;
@ColorRes
public int background;
@ColorRes
private final int backgroundDark;
private final boolean canGoForward;
private final boolean canGoBackward;
private CharSequence buttonCtaLabel = null;
@StringRes
private int buttonCtaLabelRes = 0;
private View.OnClickListener buttonCtaClickListener = null;
protected FragmentSlideMod(Builder builder) {
fragment = builder.fragment;
background = builder.background;
backgroundDark = builder.backgroundDark;
canGoForward = builder.canGoForward;
canGoBackward = builder.canGoBackward;
buttonCtaLabel = builder.buttonCtaLabel;
buttonCtaLabelRes = builder.buttonCtaLabelRes;
buttonCtaClickListener = builder.buttonCtaClickListener;
}
@Override
public Fragment getFragment() {
return fragment;
}
@Override
public void setFragment(Fragment fragment) {
this.fragment = fragment;
}
@Override
public int getBackground() {
return background;
}
@Override
public int getBackgroundDark() {
return backgroundDark;
}
@Override
public boolean canGoForward() {
if (getFragment() instanceof SlideFragment) {
return ((SlideFragment) getFragment()).canGoForward();
}
return canGoForward;
}
@Override
public boolean canGoBackward() {
if (getFragment() instanceof SlideFragment) {
return ((SlideFragment) getFragment()).canGoBackward();
}
return canGoBackward;
}
@Override
public View.OnClickListener getButtonCtaClickListener() {
if (getFragment() instanceof ButtonCtaFragment) {
return ((ButtonCtaFragment) getFragment()).getButtonCtaClickListener();
}
return buttonCtaClickListener;
}
@Override
public CharSequence getButtonCtaLabel() {
if (getFragment() instanceof ButtonCtaFragment) {
return ((ButtonCtaFragment) getFragment()).getButtonCtaLabel();
}
return buttonCtaLabel;
}
@Override
public int getButtonCtaLabelRes() {
if (getFragment() instanceof ButtonCtaFragment) {
return ((ButtonCtaFragment) getFragment()).getButtonCtaLabelRes();
}
return buttonCtaLabelRes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FragmentSlideMod that = (FragmentSlideMod) o;
if (background != that.background) return false;
if (backgroundDark != that.backgroundDark) return false;
if (canGoForward != that.canGoForward) return false;
if (canGoBackward != that.canGoBackward) return false;
if (buttonCtaLabelRes != that.buttonCtaLabelRes) return false;
if (fragment != null ? !fragment.equals(that.fragment) : that.fragment != null)
return false;
if (buttonCtaLabel != null ? !buttonCtaLabel.equals(that.buttonCtaLabel) : that.buttonCtaLabel != null)
return false;
return buttonCtaClickListener != null ? buttonCtaClickListener.equals(that.buttonCtaClickListener) : that.buttonCtaClickListener == null;
}
@Override
public int hashCode() {
int result = fragment != null ? fragment.hashCode() : 0;
result = 31 * result + background;
result = 31 * result + backgroundDark;
result = 31 * result + (canGoForward ? 1 : 0);
result = 31 * result + (canGoBackward ? 1 : 0);
result = 31 * result + (buttonCtaLabel != null ? buttonCtaLabel.hashCode() : 0);
result = 31 * result + buttonCtaLabelRes;
result = 31 * result + (buttonCtaClickListener != null ? buttonCtaClickListener.hashCode() : 0);
return result;
}
public static class Builder {
private Fragment fragment;
@ColorRes
private int background;
@ColorRes
private int backgroundDark = 0;
private boolean canGoForward = true;
private boolean canGoBackward = true;
private CharSequence buttonCtaLabel = null;
@StringRes
private int buttonCtaLabelRes = 0;
private View.OnClickListener buttonCtaClickListener = null;
public Builder fragment(Fragment fragment) {
this.fragment = fragment;
return this;
}
public Builder fragment(@LayoutRes int layoutRes, @StyleRes int themeRes) {
this.fragment = FragmentSlideFragment.newInstance(layoutRes, themeRes);
return this;
}
public Builder fragment(@LayoutRes int layoutRes) {
this.fragment = FragmentSlideFragment.newInstance(layoutRes);
return this;
}
public Builder background(@ColorRes int background) {
this.background = background;
return this;
}
public Builder backgroundDark(@ColorRes int backgroundDark) {
this.backgroundDark = backgroundDark;
return this;
}
public Builder canGoForward(boolean canGoForward) {
this.canGoForward = canGoForward;
return this;
}
public Builder canGoBackward(boolean canGoBackward) {
this.canGoBackward = canGoBackward;
return this;
}
public Builder buttonCtaLabel(CharSequence buttonCtaLabel) {
this.buttonCtaLabel = buttonCtaLabel;
this.buttonCtaLabelRes = 0;
return this;
}
public Builder buttonCtaLabelHtml(String buttonCtaLabelHtml) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.buttonCtaLabel = Html.fromHtml(buttonCtaLabelHtml, Html.FROM_HTML_MODE_LEGACY);
} else {
//noinspection deprecation
this.buttonCtaLabel = Html.fromHtml(buttonCtaLabelHtml);
}
this.buttonCtaLabelRes = 0;
return this;
}
public Builder buttonCtaLabel(@StringRes int buttonCtaLabelRes) {
this.buttonCtaLabelRes = buttonCtaLabelRes;
this.buttonCtaLabel = null;
return this;
}
public Builder buttonCtaClickListener(View.OnClickListener buttonCtaClickListener) {
this.buttonCtaClickListener = buttonCtaClickListener;
return this;
}
public FragmentSlideMod build() {
if (background == 0 || fragment == null)
throw new IllegalArgumentException("You must set at least a fragment and background.");
return new FragmentSlideMod(this);
}
}
public static class FragmentSlideFragment extends ParallaxFragment {
private static final String ARGUMENT_LAYOUT_RES =
"com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_LAYOUT_RES";
private static final String ARGUMENT_THEME_RES =
"com.heinrichreimersoftware.materialintro.SimpleFragment.ARGUMENT_THEME_RES";
public FragmentSlideFragment() {
}
public static FragmentSlideFragment newInstance(@LayoutRes int layoutRes, @StyleRes int themeRes) {
Bundle arguments = new Bundle();
arguments.putInt(ARGUMENT_LAYOUT_RES, layoutRes);
arguments.putInt(ARGUMENT_THEME_RES, themeRes);
FragmentSlideFragment fragment = new FragmentSlideFragment();
fragment.setArguments(arguments);
return fragment;
}
public static FragmentSlideFragment newInstance(@LayoutRes int layoutRes) {
return newInstance(layoutRes, 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
int themeRes = getArguments().getInt(ARGUMENT_THEME_RES);
Context contextThemeWrapper;
if (themeRes != 0) {
contextThemeWrapper = new ContextThemeWrapper(getActivity(), themeRes);
} else {
contextThemeWrapper = getActivity();
}
LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper);
return localInflater.inflate(getArguments().getInt(ARGUMENT_LAYOUT_RES), container, false);
}
}
}

View File

@ -0,0 +1,161 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import androidx.navigation.NavDestination;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.databinding.ActivityLoginBinding;
public class LoginActivity extends AppCompatActivity {
private ActivityLoginBinding b;
private App app;
private static final String TAG = "LoginActivity";
public static final int RESULT_OK = 1;
public static NavOptions navOptions;
static AppError error = null;
static List<LoginProfileObject> profileObjects;
public static boolean firstCompleted = false; // if a profile is already added during *this* login. This means that LoginChooser has to navigateUp onBackPressed. Else, finish the activity.
public static boolean privacyPolicyAccepted = false;
@Override
public void onBackPressed() {
NavDestination destination = Navigation.findNavController(this, R.id.nav_host_fragment).getCurrentDestination();
if (destination != null && destination.getId() == R.id.loginMigrationFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginMigrationSyncFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginSyncErrorFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginProgressFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginSyncFragment) {
return;
}
if (destination != null && destination.getId() == R.id.loginChooserFragment && !firstCompleted) {
setResult(RESULT_CANCELED);
finish();
return;
}
if (destination != null && destination.getId() == R.id.loginSummaryFragment) {
new MaterialDialog.Builder(this)
.title(R.string.are_you_sure)
.content(R.string.login_cancel_confirmation)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive((dialog, which) -> {
setResult(RESULT_CANCELED);
finish();
})
.show();
return;
}
Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
firstCompleted = false;
profileObjects = new ArrayList<>();
error = null;
navOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(R.anim.slide_in_left)
.setPopExitAnim(R.anim.slide_out_right)
.build();
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_login, null, false);
setContentView(b.getRoot());
app = (App) getApplication();
if (!app.appConfig.loginFinished) {
app.appConfig.miniDrawerVisible = getResources().getConfiguration().smallestScreenWidthDp > 480;
app.saveConfig("miniDrawerVisible");
}
/*b.getRoot().addOnLayoutChangeListener(((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
Animator circularReveal = null;
float finalRadius = (float) (Math.max(b.revealView.getWidth(), b.revealView.getHeight()) * 1.1);
circularReveal = ViewAnimationUtils.createCircularReveal(b.revealView, b.revealView.getWidth()/2, b.revealView.getHeight()/2, 0, finalRadius);
circularReveal.setDuration(400);
circularReveal.setInterpolator(new AccelerateInterpolator());
// make the view visible and start the animation
b.revealView.setVisibility(View.VISIBLE);
circularReveal.addListener(new Animator.AnimatorListener() {
@Override public void onAnimationEnd(Animator animation) {
Anim.fadeIn(b.title1, 500, new Animation.AnimationListener() {
@Override public void onAnimationEnd(Animation animation) {
b.title2.postDelayed(() -> {
Anim.fadeIn(b.title2, 500, new Animation.AnimationListener() {
@Override public void onAnimationEnd(Animation animation) {
b.revealView.postDelayed(() -> {
Anim.fadeOut(b.title1, 500, null);
Animation anim2 = null;
anim2 = new Anim.ResizeAnimation(b.revealView, 1.0f, 1.0f, 1.0f, 0.15f);
anim2.setDuration(800);
anim2.setInterpolator(new AccelerateDecelerateInterpolator());
b.revealView.startAnimation(anim2);
}, 700);
}
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
});
}, 1500);
}
@Override public void onAnimationStart(Animation animation) { }
@Override public void onAnimationRepeat(Animation animation) { }
});
}
@Override public void onAnimationCancel(Animator animation) { }
@Override public void onAnimationStart(Animator animation) { }
@Override public void onAnimationRepeat(Animator animation) { }
});
circularReveal.start();
}));*/
/**/
/*TextInputEditText e = findViewById(R.id.buttontest);
e.setOnClickListener((v -> {
Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
PopupMenu popup = new PopupMenu(this, e);
//popup.getMenu().add(0, 15, 0, HomeFragment.plural(c, R.plurals.time_till_seconds, 15));
//popup.getMenu().add(0, 15 * 60, 0, HomeFragment.plural(c, R.plurals.time_till_minutes, 15));
popup.getMenu().add(0, 30 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_minutes, 30));
popup.getMenu().add(0, 60 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 1));
popup.getMenu().add(0, 120 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 2));
popup.getMenu().add(0, 180 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 3));
popup.getMenu().add(0, 240 * 60, 0, HomeFragment.plural(this, R.plurals.time_till_hours, 4));
popup.setOnMenuItemClickListener(item -> {
e.setText(item.getTitle());
return false;
});
popup.show();
}));*/
}
}

View File

@ -0,0 +1,80 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.ui.modules.base.FeedbackActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding;
import static android.app.Activity.RESULT_CANCELED;
public class LoginChooserFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginChooserBinding b;
private static final String TAG = "LoginTemplate";
public LoginChooserFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_chooser, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.loginMobidziennikLogo.setOnClickListener((v) -> nav.navigate(R.id.loginMobidziennikFragment, null, LoginActivity.navOptions));
b.loginLibrusLogo.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusFragment, null, LoginActivity.navOptions));
b.loginVulcanLogo.setOnClickListener((v) -> nav.navigate(R.id.loginVulcanFragment, null, LoginActivity.navOptions));
b.loginIuczniowieLogo.setOnClickListener((v) -> nav.navigate(R.id.loginIuczniowieFragment, null, LoginActivity.navOptions));
if (LoginActivity.firstCompleted) {
// we are navigated here from LoginSummary
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> nav.navigateUp()));
}
else if (app.appConfig.lastAppVersion < 1991) {
nav.navigate(R.id.loginMigrationFragment, null, LoginActivity.navOptions);
}
else if (app.appConfig.loginFinished) {
// we are navigated here from AppDrawer
b.cancelButton.setVisibility(View.VISIBLE);
b.cancelButton.setOnClickListener((v -> {
getActivity().setResult(RESULT_CANCELED);
getActivity().finish();
}));
}
else {
// there is no profiles
b.cancelButton.setVisibility(View.GONE);
}
b.helpButton.setOnClickListener((v -> {
startActivity(new Intent(getActivity(), FeedbackActivity.class));
}));
}
}

View File

@ -0,0 +1,67 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginFinishBinding;
public class LoginFinishFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginFinishBinding b;
private static final String TAG = "LoginFinishFragment";
static boolean firstRun = true;
static int firstProfileId = -1;
public LoginFinishFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_finish, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
if (!firstRun) {
b.loginFinishSubtitle.setText(R.string.login_finish_subtitle_not_first_run);
}
b.finishButton.setOnClickListener((v -> {
Intent intent = null;
if (firstProfileId != -1) {
intent = new Intent();
intent.putExtra("profileId", firstProfileId);
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOME);
}
getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
}));
}
}

View File

@ -0,0 +1,142 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.danimahardhika.cafebar.CafeBar;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieBinding;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_SCHOOL_NAME;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
public class LoginIuczniowieFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginIuczniowieBinding b;
private static final String TAG = "LoginIuczniowie";
public LoginIuczniowieFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_iuczniowie, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
AppError error = LoginActivity.error;
if (error != null) {
switch (error.errorCode) {
case CODE_INVALID_SCHOOL_NAME:
b.loginSchoolNameLayout.setError(getString(R.string.login_error_incorrect_school_name));
break;
case CODE_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
default:
CafeBar.builder(getActivity())
.to(b.root)
.content(getString(R.string.login_error, error.asReadableString(getActivity())))
.autoDismiss(false)
.positiveText(R.string.ok)
.onPositive(CafeBar::dismiss)
.floating(true)
.swipeToDismiss(true)
.neutralText(R.string.more)
.onNeutral(cafeBar -> app.apiEdziennik.guiShowErrorDialog(getActivity(), error, R.string.error_details))
.negativeText(R.string.report)
.onNegative((cafeBar -> app.apiEdziennik.guiReportError(getActivity(), error, null)))
.show();
break;
}
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginIuczniowieHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginSchoolNameLayout.setError(null);
b.loginUsernameLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable schoolNameEditable = b.loginSchoolName.getText();
Editable usernameEditable = b.loginUsername.getText();
Editable passwordEditable = b.loginPassword.getText();
if (schoolNameEditable == null || schoolNameEditable.length() == 0) {
b.loginSchoolNameLayout.setError(getString(R.string.login_error_no_school_name));
errors = true;
}
if (usernameEditable == null || usernameEditable.length() == 0) {
b.loginUsernameLayout.setError(getString(R.string.login_error_no_username));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String schoolName = schoolNameEditable.toString().toLowerCase();
String username = usernameEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginSchoolName.setText(schoolName);
b.loginUsername.setText(username);
if (!schoolName.matches("[a-z0-9_\\-]+")) {
b.loginSchoolNameLayout.setError(getString(R.string.login_error_incorrect_school_name));
errors = true;
}
if (!username.matches("[a-z0-9_\\-]+")) {
b.loginUsernameLayout.setError(getString(R.string.login_error_incorrect_username));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_IUCZNIOWIE);
args.putString("schoolName", schoolName);
args.putString("username", username);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieHelpBinding;
public class LoginIuczniowieHelpFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginIuczniowieHelpBinding b;
private static final String TAG = "LoginIuczniowieHelp";
public LoginIuczniowieHelpFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_iuczniowie_help, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.backButton.setOnClickListener((v) -> nav.navigateUp());
}
}

View File

@ -0,0 +1,130 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.danimahardhika.cafebar.CafeBar;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusBinding;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_LIBRUS_NOT_ACTIVATED;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_LIBRUS;
public class LoginLibrusFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginLibrusBinding b;
private static final String TAG = "LoginLibrus";
public LoginLibrusFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_librus, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
AppError error = LoginActivity.error;
if (error != null) {
switch (error.errorCode) {
case CODE_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
case CODE_LIBRUS_NOT_ACTIVATED:
b.loginEmailLayout.setError(getString(R.string.login_error_account_not_activated));
break;
default:
CafeBar.builder(getActivity())
.to(b.root)
.content(getString(R.string.login_error, error.asReadableString(getActivity())))
.duration(10000)
.autoDismiss(false)
.positiveText(R.string.ok)
.onPositive(CafeBar::dismiss)
.floating(true)
.swipeToDismiss(true)
.neutralText(R.string.more)
.onNeutral(cafeBar -> app.apiEdziennik.guiShowErrorDialog(getActivity(), error, R.string.error_details))
.negativeText(R.string.report)
.onNegative((cafeBar -> app.apiEdziennik.guiReportError(getActivity(), error, null)))
.show();
break;
}
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginLibrusHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginEmailLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable emailEditable = b.loginEmail.getText();
Editable passwordEditable = b.loginPassword.getText();
if (emailEditable == null || emailEditable.length() == 0) {
b.loginEmailLayout.setError(getString(R.string.login_error_no_email));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String email = emailEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginEmail.setText(email);
if (!email.matches("([\\w.\\-_+]+)?\\w+@[\\w-_]+(\\.\\w+)+")) {
b.loginEmailLayout.setError(getString(R.string.login_error_incorrect_email));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_LIBRUS);
args.putString("email", email);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginLibrusHelpBinding;
public class LoginLibrusHelpFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginLibrusHelpBinding b;
private static final String TAG = "LoginLibrusHelp";
public LoginLibrusHelpFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_librus_help, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.backButton.setOnClickListener((v) -> nav.navigateUp());
}
}

View File

@ -0,0 +1,638 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationBinding;
import pl.szczodrzynski.edziennik.datamodels.Attendance;
import pl.szczodrzynski.edziennik.datamodels.Event;
import pl.szczodrzynski.edziennik.datamodels.EventType;
import pl.szczodrzynski.edziennik.datamodels.Grade;
import pl.szczodrzynski.edziennik.datamodels.Lesson;
import pl.szczodrzynski.edziennik.datamodels.LessonChange;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.LuckyNumber;
import pl.szczodrzynski.edziennik.datamodels.Metadata;
import pl.szczodrzynski.edziennik.datamodels.Notice;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.Subject;
import pl.szczodrzynski.edziennik.datamodels.Teacher;
import pl.szczodrzynski.edziennik.datamodels.Team;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import android.util.LongSparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_CLASS_EVENT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_ESSAY;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_EXAM;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_EXCURSION;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_INFORMATION;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_PROJECT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_PT_MEETING;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_READING;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_SHORT_QUIZ;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_CLASS_EVENT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_ESSAY;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_EXAM;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_EXCURSION;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_INFORMATION;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_PROJECT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_PT_MEETING;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_READING;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_SHORT_QUIZ;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_DEMO;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_LIBRUS;
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.Profile.REGISTRATION_DISABLED;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_UNSPECIFIED;
public class LoginMigrationFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginMigrationBinding b;
private static final String TAG = "LoginMigration";
public LoginMigrationFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_migration, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
String profilesStr;
if (app.appConfig.lastAppVersion == 198 && (profilesStr = app.appSharedPrefs.getString("app.appConfig.profiles", null)) != null) {
Toast.makeText(app, getString(R.string.login_migration_toast), Toast.LENGTH_SHORT).show();
AsyncTask.execute(() -> {
try {
migrate(profilesStr);
}
catch (Exception e) {
getActivity().runOnUiThread(() -> {
new MaterialDialog.Builder(getActivity())
.title(R.string.error_occured)
.content(getString(R.string.login_migration_error_format))
.positiveText(R.string.ok)
.onPositive(((dialog, which) -> {
dialog.dismiss();
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}))
.autoDismiss(false)
.canceledOnTouchOutside(false)
.show();
});
}
finally {
if (app.db.profileDao().getIdsNow().size() != 0) {
app.appSharedPrefs.edit().remove("app.appConfig.profiles").apply();
}
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE;
app.saveConfig("lastAppVersion");
}
});
}
b.doneButton.setOnClickListener((v -> {
nav.navigate(R.id.loginMigrationSyncFragment, null, LoginActivity.navOptions);
}));
}
private String gp(SharedPreferences p, String key, String defValue) {
String s = p.getString(key, defValue);
if (s == null)
s = defValue;
return s;
}
private void migrate(String profilesStr) {
Context c = getContext();
if (c == null)
return;
List<Metadata> metadataList = new ArrayList<>();
JsonArray profiles = new JsonParser().parse(profilesStr).getAsJsonArray();
Map<Integer, JsonObject> loginStores = app.gson.fromJson(app.appSharedPrefs.getString("app.appConfig.loginStores", "{}"), new TypeToken<Map<Integer, JsonObject>>(){}.getType());
if (loginStores == null) {
Toast.makeText(c, "Błąd odczytywania słoików z danymi.", Toast.LENGTH_SHORT).show();
return;
}
for (JsonElement profileEl: profiles) {
JsonObject jProfile = profileEl.getAsJsonObject();
int profileId = jProfile.get("id").getAsInt();
SharedPreferences p = app.getSharedPreferences(String.format(getString(R.string.preference_file_register), profileId), Context.MODE_PRIVATE);
if (!Boolean.parseBoolean(p.getString("app.register.loggedIn", Boolean.toString(false))))
return;
Profile profile = new Profile();
profile.setId(profileId);
profile.setName(jProfile.get("name").getAsString());
profile.setSubname(jProfile.get("subname").getAsString());
profile.setImage(jProfile.get("image").getAsString());
profile.setSyncEnabled(Boolean.parseBoolean(p.getString("app.register.syncThisProfile", Boolean.toString(profile.getSyncEnabled()))));
profile.setSyncNotifications(Boolean.parseBoolean(p.getString("app.register.syncNotificationsEnabled", Boolean.toString(profile.getSyncNotifications()))));
profile.setEnableSharedEvents(Boolean.parseBoolean(p.getString("app.register.eventsShared", Boolean.toString(profile.getEnableSharedEvents()))));
app.appConfig.countInSeconds = Boolean.parseBoolean(p.getString("app.register.countInSeconds", Boolean.toString(app.appConfig.countInSeconds)));
profile.setLoggedIn(true);
// so in some APIs we force a full, clean sync
profile.setEmpty(true);//Boolean.parseBoolean(p.getString("app.register.empty", Boolean.toString(profile.empty)));
profile.setArchived(false);
String s;
s = gp(p, "app.register.studentNameLong", "\"\"");
profile.setStudentNameLong(s.replace("\"", ""));
s = gp(p, "app.register.studentNameShort", "\"\"");
profile.setStudentNameShort(s.replace("\"", ""));
profile.setStudentNumber(Integer.parseInt(gp(p, "app.register.studentNumber", "-1")));
profile.setStudentData(new JsonParser().parse(gp(p, "app.register.studentStore", "[]")).getAsJsonObject());
boolean autoRegistrationDecided = Boolean.parseBoolean(p.getString("app.register.autoRegistrationDecided", "true"));
boolean autoRegistrationAllowed = Boolean.parseBoolean(p.getString("app.register.autoRegistrationAllowed", "true"));
profile.setRegistration(!autoRegistrationAllowed && !autoRegistrationDecided ? REGISTRATION_UNSPECIFIED : !autoRegistrationAllowed ? REGISTRATION_DISABLED : REGISTRATION_ENABLED);
profile.setGradeColorMode(Integer.parseInt(gp(p, "app.register.gradeColorMode", "1")));
profile.setAgendaViewType(Integer.parseInt(gp(p, "app.register.agendaViewType", "0")));
profile.setCurrentSemester(Integer.parseInt(gp(p, "app.register.currentSemester", "1")));
profile.setAttendancePercentage(Float.parseFloat(gp(p, "app.register.attendancePercentage", "0.0")));
profile.setDateSemester1Start(app.gson.fromJson(gp(p, "app.register.dateSemester1Start", ""), Date.class));
profile.setDateSemester2Start(app.gson.fromJson(gp(p, "app.register.dateSemester2Start", ""), Date.class));
profile.setDateYearEnd(app.gson.fromJson(gp(p, "app.register.dateYearEnd", ""), Date.class));
profile.setLuckyNumberEnabled(Boolean.parseBoolean(gp(p, "app.register.luckyNumberEnabled", Boolean.toString(profile.getLuckyNumberEnabled()))));
profile.setLuckyNumber(Integer.parseInt(gp(p, "app.register.luckyNumber", "-1")));
profile.setLuckyNumberDate(app.gson.fromJson(gp(p, "app.register.luckyNumberDate", ""), Date.class));
profile.setLoginStoreId(jProfile.get("loginStoreId").getAsInt());
LoginStore loginStore = new LoginStore(
profile.getLoginStoreId(),
Integer.parseInt(gp(p, "app.register.loginType", "1")),
loginStores.get(profile.getLoginStoreId())
);
String teamPrefix;
switch (loginStore.type) {
case LOGIN_TYPE_MOBIDZIENNIK:
teamPrefix = loginStore.getLoginData("serverName", "MOBI_UN");
break;
case LOGIN_TYPE_LIBRUS:
teamPrefix = profile.getStudentData("schoolName", "LIBRUS_UN");
break;
case LOGIN_TYPE_IUCZNIOWIE:
teamPrefix = loginStore.getLoginData("schoolName", "IUCZNIOWIE_UN");
break;
case LOGIN_TYPE_VULCAN:
teamPrefix = profile.getStudentData("schoolName", "VULCAN_UN");
break;
case LOGIN_TYPE_DEMO:
teamPrefix = loginStore.getLoginData("serverName", "DEMO_UN");
break;
default:
teamPrefix = "TYPE_UNKNOWN";
break;
}
JsonArray items = new JsonParser().parse(gp(p, "app.register.users", "[]")).getAsJsonArray();
if (items != null) {
List<Teacher> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
if (item.get("id").getAsLong() == -1)
continue;
itemList.add(new Teacher(
profileId,
item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0),
item.get("name").getAsString(),
item.get("surname").getAsString()
));
}
app.db.teacherDao().addAllIgnore(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.subjects", "[]")).getAsJsonArray();
if (items != null) {
List<Subject> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
if (item.get("id").getAsLong() == -1)
continue;
itemList.add(new Subject(
profileId,
item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0),
item.get("longName").getAsString(),
item.get("shortName").getAsString()
));
}
app.db.subjectDao().addAll(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.teams", "[]")).getAsJsonArray();
List<Team> tItemList = new ArrayList<>();
if (items != null) {
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
if (item.get("id").getAsLong() == -1)
continue;
tItemList.add(new Team(
profileId,
item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0),
item.get("name").getAsString(),
item.get("type").getAsInt(),
teamPrefix+":"+item.get("name").getAsString(),
item.get("teacherId").getAsInt()
));
}
}
JsonObject tItem = new JsonParser().parse(gp(p, "app.register.teamClass", "")).getAsJsonObject();
tItemList.add(new Team(
profileId,
tItem.get("id").getAsLong(),
tItem.get("name").getAsString(),
tItem.get("type").getAsInt(),
teamPrefix+":"+tItem.get("name").getAsString(),
tItem.get("teacherId").getAsInt()
));
app.db.teamDao().addAll(tItemList);
Map<Integer, Pair<String, Integer>> types = app.gson.fromJson(p.getString("app.register.eventTypes", "{}"), new TypeToken<Map<Integer, Pair<String, Integer>>>(){}.getType());
if (types != null) {
for (Integer index : types.keySet()) {
Pair<String, Integer> type = types.get(index);
if (type != null && type.second != null) {
int color = type.second;
switch (color) {
case 0xffdaa520:
color = COLOR_DEFAULT;
break;
case 0xffff0000:
color = COLOR_EXAM;
break;
case 0xffadff2f:
color = COLOR_SHORT_QUIZ;
break;
case 0xff4050b5:
color = COLOR_ESSAY;
break;
case 0xff673ab7:
color = COLOR_PROJECT;
break;
case 0xffabcdef:
color = COLOR_PT_MEETING;
break;
case 0xff4caf50:
color = COLOR_EXCURSION;
break;
case 0xffffeb3b:
color = COLOR_READING;
break;
}
app.db.eventTypeDao().add(new EventType(profileId, index, type.first, color));
}
}
}
items = new JsonParser().parse(gp(p, "app.register.events", "[]")).getAsJsonArray();
if (items != null) {
List<Event> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
int color = item.get("color").getAsInt();
switch (color) {
case 0xffdaa520:
case 0xffff0000:
case 0xffadff2f:
case 0xff4050b5:
case 0xff673ab7:
case 0xffabcdef:
case 0xff4caf50:
case 0xffffeb3b:
color = -1;
break;
}
long id = item.get("id").getAsLong();
Event itemObj = new Event(
profileId,
id,
app.gson.fromJson(item.get("eventDate"), Date.class),
app.gson.fromJson(item.get("startTime"), Time.class),
item.get("topic").getAsString(),
color,
item.get("type").getAsInt(),
(loginStore.type != LOGIN_TYPE_MOBIDZIENNIK || id >= 1420070400000L) && item.get("addedManually").getAsBoolean(),
item.get("teacherId").getAsInt(),
item.get("subjectId").getAsInt(),
item.get("teamId").getAsInt());
if (item.get("sharedBy") != null) {
itemObj.sharedBy = item.get("sharedBy").getAsString();
itemObj.sharedByName = item.get("sharedByName").getAsString();
}
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_EVENT,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
item.get("addedDate").getAsLong()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.eventDao().addAll(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.homeworksNew", "[]")).getAsJsonArray();
if (items != null) {
List<Event> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
Event itemObj = new Event(
profileId,
item.get("id").getAsLong(),
app.gson.fromJson(item.get("eventDate"), Date.class),
app.gson.fromJson(item.get("startTime"), Time.class),
item.get("topic").getAsString(),
-1,
TYPE_HOMEWORK,
item.get("addedManually").getAsBoolean(),
item.get("teacherId").getAsInt(),
item.get("subjectId").getAsInt(),
item.get("teamId").getAsInt());
if (item.get("sharedBy") != null) {
itemObj.sharedBy = item.get("sharedBy").getAsString();
itemObj.sharedByName = item.get("sharedByName").getAsString();
}
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_HOMEWORK,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
item.get("addedDate").getAsLong()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.eventDao().addAll(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.gradeCategories", "[]")).getAsJsonArray();
LongSparseArray<Pair<String, Integer>> gradeCategories = new LongSparseArray<>();
if (items != null) {
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
if (item.get("id").getAsLong() == -1)
continue;
gradeCategories.put(item.get("id").getAsLong(),
new Pair<>(item.get("description").getAsString(), item.get("color").getAsInt()));
}
}
items = new JsonParser().parse(gp(p, "app.register.grades", "[]")).getAsJsonArray();
if (items != null) {
List<Grade> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
long categoryId = item.get("categoryId").getAsInt();
Pair<String, Integer> category = gradeCategories.get(categoryId);
Grade itemObj = new Grade(
profileId,
item.get("id").getAsLong(),
category != null ? category.first : "",
category != null && category.second != null ? category.second : 0xff0000ff,
item.get("description").getAsString(),
item.get("name").getAsString(),
item.get("value").getAsFloat(),
item.get("weight").getAsInt(),
item.get("semester").getAsInt(),
item.get("teacherId").getAsInt(),
item.get("subjectId").getAsInt());
itemObj.type = item.get("type").getAsInt();
if (loginStore.type == LOGIN_TYPE_IUCZNIOWIE && itemObj.type != 0)
continue;
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_GRADE,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
item.get("addedDate").getAsLong()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.gradeDao().addAll(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.notices", "[]")).getAsJsonArray();
if (items != null) {
List<Notice> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
Notice itemObj = new Notice(
profileId,
item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0),
item.get("reason").getAsString(),
item.get("semester").getAsInt(),
item.get("type").getAsInt(),
item.get("teacherId").getAsInt());
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_NOTICE,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
item.get("addedDate").getAsLong()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.noticeDao().addAll(itemList);
}
items = new JsonParser().parse(gp(p, "app.register.attendances", "[]")).getAsJsonArray();
if (items != null) {
List<Attendance> itemList = new ArrayList<>();
for (JsonElement itemEl : items) {
JsonObject item = itemEl.getAsJsonObject();
Attendance itemObj = new Attendance(
profileId,
item.get("id").getAsLong(),
item.get("teacherId").getAsInt(),
item.get("subjectId").getAsInt(),
item.get("semester").getAsInt(),
item.get("lessonTopic").getAsString(),
app.gson.fromJson(item.get("lessonDate"), Date.class),
app.gson.fromJson(item.get("startTime"), Time.class),
item.get("type").getAsInt());
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
item.get("addedDate").getAsLong()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.attendanceDao().addAll(itemList);
}
tItem = new JsonParser().parse(gp(p, "app.register.timetable", "{weekdays: [], lessonChanges: [], lessonAdditions: []}")).getAsJsonObject();
if (tItem != null) {
List<Lesson> itemLessonList = new ArrayList<>();
for (JsonElement weekDayEl : tItem.getAsJsonArray("weekdays")) {
JsonObject weekDay = weekDayEl.getAsJsonObject();
int weekDayNum = weekDay.get("weekDay").getAsInt();
for (JsonElement itemEl : weekDay.getAsJsonArray("lessons")) {
JsonObject item = itemEl.getAsJsonObject();
Lesson itemObj = new Lesson(
profileId,
weekDayNum,
app.gson.fromJson(item.get("startTime"), Time.class),
app.gson.fromJson(item.get("endTime"), Time.class)
);
itemObj.classroomName = item.get("classroomName").getAsString();
itemObj.subjectId = item.get("subjectId").getAsInt();
itemObj.teacherId = item.get("teacherId").getAsInt();
itemObj.teamId = item.get("teamId").getAsInt();
itemLessonList.add(itemObj);
}
}
app.db.lessonDao().addAll(itemLessonList);
List<LessonChange> itemList = new ArrayList<>();
for (JsonElement itemEl : tItem.getAsJsonArray("lessonChanges")) {
JsonObject item = itemEl.getAsJsonObject();
LessonChange itemObj = new LessonChange(
profileId,
app.gson.fromJson(item.get("lessonDate"), Date.class),
app.gson.fromJson(item.get("startTime"), Time.class),
app.gson.fromJson(item.get("endTime"), Time.class));
itemObj.classroomName = item.get("classroomName").getAsString();
itemObj.subjectId = item.get("subjectId").getAsInt();
itemObj.teacherId = item.get("teacherId").getAsInt();
itemObj.teamId = item.get("teamId").getAsInt();
itemObj.type = item.get("type").getAsInt();
Metadata metadataObj = new Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
itemObj.id,
item.get("seen").getAsBoolean(),
item.get("notified").getAsBoolean(),
System.currentTimeMillis()
);
if (itemObj.id == -1)
continue;
itemList.add(itemObj);
metadataList.add(metadataObj);
}
app.db.lessonChangeDao().addAll(itemList);
}
app.db.eventTypeDao().add(new EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT));
app.db.eventTypeDao().add(new EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION));
if (profile.getLuckyNumberDate() != null) {
app.db.luckyNumberDao().add(new LuckyNumber(profile.getId(), profile.getLuckyNumberDate(), profile.getLuckyNumber()));
}
app.db.profileDao().add(profile);
app.db.loginStoreDao().add(loginStore);
app.db.metadataDao().addAllIgnore(metadataList);
}
try {
app.appConfig.appInstalledTime = app.getPackageManager().getPackageInfo(app.getPackageName(), 0).firstInstallTime;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
app.appConfig.appRateSnackbarTime = app.appConfig.appInstalledTime + 2 * 24 * 60 * 60 * 1000;
app.appConfig.loginFinished = true;
app.saveConfig("loginFinished", "appInstalledTime", "appRateSnackbarTime");
}
}

View File

@ -0,0 +1,132 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationSyncBinding;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.sync.SyncJob;
import pl.szczodrzynski.edziennik.sync.SyncService;
public class LoginMigrationSyncFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginMigrationSyncBinding b;
private static final String TAG = "LoginMigrationSync";
private List<String> profileNameList = new ArrayList<>();
private int profileIndex = 0;
public LoginMigrationSyncFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_migration_sync, container, false);
return b.getRoot();
}
private void begin() {
AsyncTask.execute(() -> {
if (getActivity() == null) {
return;
}
for (Profile profileObject: app.db.profileDao().getAllNow()) {
profileNameList.add(profileObject.getName());
}
getActivity().runOnUiThread(() -> {
profileIndex = 0;
b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileNameList.size() > profileIndex ? profileNameList.get(profileIndex) : " ")));
});
SyncJob.run(app, -1, -1);
});
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
SyncService.customCallback = new SyncCallback() {
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
@Override
public void onSuccess(Context activityContext, ProfileFull profileFull) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
if (profileFull != null) {
// a profile is finished
profileIndex++;
b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileIndex < profileNameList.size() ? profileNameList.get(profileIndex) : profileNameList.get(profileNameList.size()-1))));
}
else {
// all profiles are finished
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}
});
}
@Override
public void onError(Context activityContext, AppError error) {
if (getActivity() == null)
return;
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}
@Override
public void onProgress(int progressStep) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
b.loginSyncProgressBar.setMax(SyncService.maxProgress);
b.loginSyncProgressBar.setProgress(SyncService.progress);
});
}
@Override
public void onActionStarted(int stringResId) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
b.loginSyncSubtitle2.setText(getString(R.string.login_sync_subtitle_2_format, getString(stringResId)));
});
}
};
begin();
}
}

View File

@ -0,0 +1,148 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.danimahardhika.cafebar.CafeBar;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikBinding;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_ARCHIVED;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_LOGIN;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_SERVER_ADDRESS;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_OLD_PASSWORD;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class LoginMobidziennikFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginMobidziennikBinding b;
private static final String TAG = "LoginMobidziennik";
public LoginMobidziennikFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_mobidziennik, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
AppError error = LoginActivity.error;
if (error != null) {
switch (error.errorCode) {
case CODE_INVALID_LOGIN:
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
break;
case CODE_OLD_PASSWORD:
b.loginPasswordLayout.setError(getString(R.string.login_error_old_password));
break;
case CODE_ARCHIVED:
b.loginUsernameLayout.setError(getString(R.string.sync_error_archived));
break;
case CODE_INVALID_SERVER_ADDRESS:
b.loginServerAddressLayout.setError(getString(R.string.login_error_incorrect_address));
break;
default:
CafeBar.builder(getActivity())
.to(b.root)
.content(getString(R.string.login_error, error.asReadableString(getActivity())))
.autoDismiss(false)
.positiveText(R.string.ok)
.onPositive(CafeBar::dismiss)
.floating(true)
.swipeToDismiss(true)
.neutralText(R.string.more)
.onNeutral(cafeBar -> app.apiEdziennik.guiShowErrorDialog(getActivity(), error, R.string.error_details))
.negativeText(R.string.report)
.onNegative((cafeBar -> app.apiEdziennik.guiReportError(getActivity(), error, null)))
.show();
break;
}
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginMobidziennikHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginServerAddressLayout.setError(null);
b.loginUsernameLayout.setError(null);
b.loginPasswordLayout.setError(null);
Editable serverNameEditable = b.loginServerAddress.getText();
Editable usernameEditable = b.loginUsername.getText();
Editable passwordEditable = b.loginPassword.getText();
if (serverNameEditable == null || serverNameEditable.length() == 0) {
b.loginServerAddressLayout.setError(getString(R.string.login_error_no_address));
errors = true;
}
if (usernameEditable == null || usernameEditable.length() == 0) {
b.loginUsernameLayout.setError(getString(R.string.login_error_no_login));
errors = true;
}
if (passwordEditable == null || passwordEditable.length() == 0) {
b.loginPasswordLayout.setError(getString(R.string.login_error_no_password));
errors = true;
}
if (errors)
return;
errors = false;
String serverName = serverNameEditable.toString().toLowerCase().replaceAll("(?:http://|www.|mobidziennik\\.pl|wizja\\.net|\\.)", "");
String username = usernameEditable.toString().toLowerCase();
String password = passwordEditable.toString();
b.loginServerAddress.setText(serverName);
b.loginUsername.setText(username);
if (!serverName.matches("^[a-z0-9_\\-]+$")) {
b.loginServerAddressLayout.setError(getString(R.string.login_error_incorrect_address));
errors = true;
}
if (!username.matches("^[a-z0-9_\\-@+.]+$")) {
b.loginUsernameLayout.setError(getString(R.string.login_error_incorrect_login));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_MOBIDZIENNIK);
args.putString("serverName", serverName);
args.putString("username", username);
args.putString("password", password);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikHelpBinding;
public class LoginMobidziennikHelpFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginMobidziennikHelpBinding b;
private static final String TAG = "LoginMobidziennikHelp";
public LoginMobidziennikHelpFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_mobidziennik_help, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.backButton.setOnClickListener((v) -> nav.navigateUp());
}
}

View File

@ -0,0 +1,29 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Profile;
public class LoginProfileObject {
LoginStore loginStore = null;
List<Profile> profileList = new ArrayList<>();
List<Boolean> selectedList = new ArrayList<>();
public LoginProfileObject(@NonNull LoginStore loginStore, @NonNull List<Profile> profileList) {
this.loginStore = loginStore;
this.profileList = profileList;
for (Profile ignored : profileList) {
selectedList.add(true);
}
}
public LoginProfileObject addProfile(Profile profile) {
profileList.add(profile);
selectedList.add(true);
return this;
}
}

View File

@ -0,0 +1,202 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.gson.JsonObject;
import java.util.List;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.Edziennik;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER;
public class LoginProgressFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginProgressBinding b;
private static final String TAG = "LoginProgress";
public LoginProgressFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_progress, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
Bundle args = getArguments();
LoginActivity.error = null;
if (args == null) {
LoginActivity.error = new AppError(TAG, 72, CODE_OTHER, getString(R.string.login_error_no_arguments));
nav.navigateUp();
return;
}
int loginType = args.getInt("loginType", -1);
LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject());
loginStore.copyFrom(args);
Edziennik.getApi(app, loginType).sync(getActivity(), new SyncCallback() {
@Override
public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) {
// because these callbacks are always on a worker thread
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
LoginActivity.profileObjects.add(new LoginProfileObject(
loginStore,
profileList));
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions);
});
}
}
@Override
public void onSuccess(Context activityContext, ProfileFull profileFull) {
}
@Override
public void onError(Context activityContext, AppError error) {
LoginActivity.error = error;
// because these callbacks are always on a worker thread
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
nav.navigateUp();
});
}
@Override
public void onProgress(int progressStep) {
}
@Override
public void onActionStarted(int stringResId) {
}
}, -1, null, loginStore);
/*if (true)
return;
JsonObject loginData = new JsonObject();
loginData.addProperty("serverName", b.loginServerAddress.getText().toString());
loginData.addProperty("username", b.loginUsername.getText().toString());
loginData.addProperty("password", b.loginPassword.getText().toString());
getApi(app, LOGIN_TYPE_MOBIDZIENNIK).sync(getActivity(), new Edziennik.DataCallback() {
@Override
public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) {
int profileId = app.profileLastId()+1;
if (profileList.size() == 1) {
Profile profile = profileList.get(0);
saveProfile(profile, loginStore, profileId, profileId);
finishSaving();
return;
}
List<String> profileNames = new ArrayList<>();
for (Profile profile: profileList) {
profileNames.add(profile.name);
}
new MaterialDialog.Builder(getActivity())
.title(R.string.sync_multiaccount_select_students)
.items(profileNames)
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.neutralText(R.string.help)
.autoDismiss(false)
.canceledOnTouchOutside(false)
.onNeutral((dialog, which) ->
new MaterialDialog.Builder(getActivity())
.title(R.string.help)
.content(R.string.sync_multiaccount_select_students_text)
.positiveText(R.string.ok)
.show()
)
.onNegative(((dialog, which) -> dialog.dismiss()))
.itemsCallbackMultiChoice(null, (dialog, which, text) -> {
// create new profiles, then restart the application or sth
if (text.length < 1 || which.length < 1) {
Toast.makeText(app, R.string.sync_multiaccount_select_students_error, Toast.LENGTH_SHORT).show();
return false;
}
dialog.dismiss();
int pos = 0;
for (int index: which) {
Profile profile = profileList.get(index);
saveProfile(profile, loginStore, profileId+(pos++), profileId);
}
finishSaving();
String list = "";
for (ProfileFull profileFull: app.db.profileDao().getAllFullNow()) {
d(TAG, profileFull.toString());
list += profileFull.studentNameLong+" student ID "+profileFull.getStudentData("studentId", -1)+"\n";
}
d(TAG, loginStore.toString());
list += loginStore.getLoginData("username", "(NO USERNAME)")+"\n";
new MaterialDialog.Builder(getActivity())
.title("Znaleziono profile")
.content(list)
.positiveText("OK")
.show();
return false;
})
.show();
}
@Override
public void onSuccess(Context activityContext, ProfileFull profile) {
Toast.makeText(activityContext, "Zakończono", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Context activityContext, int errorCode, String errorText, Throwable throwable, String apiResponse) {
}
@Override
public void onProgress(int progressStep) {
}
}, -1, null, new LoginStore(-1, LOGIN_TYPE_MOBIDZIENNIK, loginData));*/
}
}

View File

@ -0,0 +1,201 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSummaryBinding;
import pl.szczodrzynski.edziennik.databinding.RowLoginProfileListItemBinding;
import pl.szczodrzynski.edziennik.datamodels.Profile;
public class LoginSummaryFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginSummaryBinding b;
private static final String TAG = "LoginSummary";
public LoginSummaryFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_summary, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
LoginActivity.firstCompleted = true;
List<ItemProfileModel> profileList = new ArrayList<>();
int index = 0;
for (LoginProfileObject profileObject: LoginActivity.profileObjects) {
int subIndex = 0;
for (Profile profile: profileObject.profileList) {
ItemProfileModel profileModel = new ItemProfileModel(
index,
subIndex,
profile.getName(),
profileObject.loginStore.type,
profileObject.selectedList.get(subIndex)
);
profileList.add(profileModel);
subIndex++;
}
index++;
}
b.profileListView.setLayoutManager(new LinearLayoutManager(getContext()));
b.profileListView.setAdapter(new ProfileListAdapter(profileList));
b.registerMeSwitch.setOnCheckedChangeListener(((buttonView, isChecked) -> {
if (!isChecked) {
new MaterialDialog.Builder(getActivity())
.title(R.string.login_summary_unregister_title)
.content(R.string.login_summary_unregister_text)
.positiveText(R.string.yes)
.negativeText(R.string.cancel)
.onNegative(((dialog, which) -> {
b.registerMeSwitch.setChecked(true);
}))
.show();
}
}));
b.anotherButton.setOnClickListener((v -> nav.navigate(R.id.loginChooserFragment, null, LoginActivity.navOptions)));
b.finishButton.setOnClickListener(v -> {
if (LoginActivity.privacyPolicyAccepted) {
Bundle args = new Bundle();
args.putBoolean("registrationAllowed", b.registerMeSwitch.isChecked());
nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions);
return;
}
boolean profileSelected = true;
for (LoginProfileObject profileObject: LoginActivity.profileObjects) {
if (profileObject.selectedList.size() == 0 && profileSelected)
profileSelected = false;
}
if (!profileSelected) {
new MaterialDialog.Builder(getActivity())
.title(R.string.login_summary_no_profiles_title)
.content(R.string.login_summary_no_profiles_text)
.positiveText(R.string.ok)
.show();
return;
}
new MaterialDialog.Builder(getActivity())
.title(R.string.privacy_policy)
.content(Html.fromHtml("Korzystając z aplikacji potwierdzasz <a href=\"http://szkolny.eu/privacy-policy\">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia."))
.positiveText(R.string.i_agree)
.neutralText(R.string.i_disagree)
.onPositive(((dialog, which) -> {
Bundle args = new Bundle();
args.putBoolean("registrationAllowed", b.registerMeSwitch.isChecked());
nav.navigate(R.id.loginSyncFragment, args, LoginActivity.navOptions);
}))
.show();
});
}
class ItemProfileModel {
int listIndex;
int listSubIndex;
String name;
int loginType;
boolean selected;
public ItemProfileModel(int listIndex, int listSubIndex, String name, int loginType, boolean selected) {
this.listIndex = listIndex;
this.listSubIndex = listSubIndex;
this.name = name;
this.loginType = loginType;
this.selected = selected;
}
}
public class ProfileListAdapter extends RecyclerView.Adapter<ProfileListAdapter.ViewHolder> {
private List<ItemProfileModel> profileList;
public ProfileListAdapter(List<ItemProfileModel> profileList) {
this.profileList = profileList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RowLoginProfileListItemBinding b = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.row_login_profile_list_item, parent, false);
return new ViewHolder(b);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
RowLoginProfileListItemBinding b = holder.b;
ItemProfileModel m = profileList.get(position);
b.textView.setText(m.name);
b.checkBox.setChecked(m.selected);
b.checkBox.jumpDrawablesToCurrentState();
View.OnClickListener onClickListener = v -> {
if (v instanceof CheckBox) {
m.selected = ((CheckBox) v).isChecked();
} else {
m.selected = !m.selected;
b.checkBox.setChecked(m.selected);
b.checkBox.jumpDrawablesToCurrentState();
}
LoginActivity.profileObjects.get(m.listIndex).selectedList.set(m.listSubIndex, m.selected);
};
b.checkBox.setOnClickListener(onClickListener);
b.getRoot().setOnClickListener(onClickListener);
//b.root.setOnClickListener(onClickListener);
//holder.bind(b.textView, onClickListener);
}
@Override
public int getItemCount() {
return profileList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RowLoginProfileListItemBinding b;
public ViewHolder(@NonNull RowLoginProfileListItemBinding b) {
super(b.getRoot());
this.b = b;
}
}
}
}

View File

@ -0,0 +1,58 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncErrorBinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LoginSyncErrorFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginSyncErrorBinding b;
private static final String TAG = "LoginSyncError";
public LoginSyncErrorFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_sync_error, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.errorDetails.setText(LoginActivity.error == null ? "" : LoginActivity.error.asReadableString(getActivity()));
b.reportButton.setOnClickListener((v -> {
app.apiEdziennik.guiReportError(getActivity(), LoginActivity.error, null);
}));
b.nextButton.setOnClickListener((v -> {
nav.navigate(R.id.loginFinishFragment, null, LoginActivity.navOptions);
}));
}
}

View File

@ -0,0 +1,227 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding;
import pl.szczodrzynski.edziennik.datamodels.EventType;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.sync.SyncJob;
import pl.szczodrzynski.edziennik.sync.SyncService;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_CLASS_EVENT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_EXAM;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_EXCURSION;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_INFORMATION;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_PROJECT;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_PT_MEETING;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_READING;
import static pl.szczodrzynski.edziennik.datamodels.Event.COLOR_SHORT_QUIZ;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_CLASS_EVENT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_DEFAULT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_ESSAY;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_EXAM;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_EXCURSION;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_INFORMATION;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_PROJECT;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_PT_MEETING;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_READING;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_SHORT_QUIZ;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_DISABLED;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_UNSPECIFIED;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class LoginSyncFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginSyncBinding b;
private static final String TAG = "LoginSyncFragment";
private List<String> profileNameList = new ArrayList<>();
private int profileIndex = 0;
public LoginSyncFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_sync, container, false);
return b.getRoot();
}
private void begin() {
AsyncTask.execute(() -> {
if (getActivity() == null) {
return;
}
int profileId = app.profileLastId()+1;
final int firstProfileId = profileId;
int loginStoreId = profileId;
// profileId contains the first ID free to use
for (LoginProfileObject profileObject: LoginActivity.profileObjects) {
int subIndex = 0;
for (Profile profile: profileObject.profileList) {
if (profileObject.selectedList.get(subIndex)) {
saveProfile(
profile,
profileObject.loginStore,
profileId,
loginStoreId
);
profileNameList.add(profile.getName());
profileId++;
}
subIndex++;
}
loginStoreId = profileId;
}
for (Profile profile: app.db.profileDao().getAllNow()) {
d(TAG, profile.toString());
}
for (LoginStore loginStore: app.db.loginStoreDao().getAllNow()) {
d(TAG, loginStore.toString());
}
if (app.appConfig.loginFinished) {
LoginFinishFragment.firstRun = false;
}
else {
LoginFinishFragment.firstRun = true;
app.appConfig.loginFinished = true;
app.saveConfig("loginFinished");
}
LoginFinishFragment.firstProfileId = firstProfileId;
getActivity().runOnUiThread(() -> {
profileIndex = 0;
b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileNameList.size() > profileIndex ? profileNameList.get(profileIndex) : " ")));
});
SyncJob.run(app, firstProfileId, -1);
});
}
private void saveProfile(Profile profile, LoginStore loginStore, int profileId, int loginStoreId) {
profile.setRegistration(REGISTRATION_UNSPECIFIED);
if (getArguments() != null) {
if (getArguments().getBoolean("registrationAllowed", false)) {
profile.setRegistration(REGISTRATION_ENABLED);
}
else {
profile.setRegistration(REGISTRATION_DISABLED);
}
}
profile.setId(profileId);
profile.setLoginStoreId(loginStoreId);
loginStore.id = loginStoreId;
List<EventType> typeList = new ArrayList<>();
typeList.add(new EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK));
typeList.add(new EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT));
typeList.add(new EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM));
typeList.add(new EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ));
typeList.add(new EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ));
typeList.add(new EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT));
typeList.add(new EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING));
typeList.add(new EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION));
typeList.add(new EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING));
typeList.add(new EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT));
typeList.add(new EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION));
app.db.eventTypeDao().addAll(typeList);
app.profileSaveFull(profile, loginStore);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
LoginActivity.error = null;
SyncService.customCallback = new SyncCallback() {
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
@Override
public void onSuccess(Context activityContext, ProfileFull profileFull) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
if (profileFull != null) {
// a profile is finished
profileIndex++;
b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileIndex < profileNameList.size() ? profileNameList.get(profileIndex) : profileNameList.get(profileNameList.size()-1))));
}
else {
// all profiles are finished
nav.navigate(R.id.loginFinishFragment, null , LoginActivity.navOptions);
}
});
}
@Override
public void onError(Context activityContext, AppError error) {
LoginActivity.error = error;
if (getActivity() != null) {
getActivity().runOnUiThread(() -> {
nav.navigate(R.id.loginSyncErrorFragment, null, LoginActivity.navOptions);
});
}
}
@Override
public void onProgress(int progressStep) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
b.loginSyncProgressBar.setMax(SyncService.maxProgress);
b.loginSyncProgressBar.setProgress(SyncService.progress);
});
}
@Override
public void onActionStarted(int stringResId) {
if (getActivity() == null)
return;
getActivity().runOnUiThread(() -> {
b.loginSyncSubtitle2.setText(getString(R.string.login_sync_subtitle_2_format, getString(stringResId)));
});
}
};
begin();
}
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginTemplateBinding;
public class LoginTemplateFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginTemplateBinding b;
private static final String TAG = "LoginTemplate";
public LoginTemplateFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_template, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
}
}

View File

@ -0,0 +1,216 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
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 androidx.navigation.NavController;
import androidx.navigation.Navigation;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.ui.modules.webpush.QrScannerActivity;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginVulcanBinding;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_EXPIRED_TOKEN;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_PIN;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_SYMBOL;
import static pl.szczodrzynski.edziennik.api.AppError.CODE_INVALID_TOKEN;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_VULCAN;
public class LoginVulcanFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginVulcanBinding b;
private static final String TAG = "LoginVulcan";
public LoginVulcanFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_vulcan, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
view.postDelayed(() -> {
AppError error = LoginActivity.error;
if (error != null) {
switch (error.errorCode) {
case CODE_INVALID_TOKEN:
b.loginTokenLayout.setError(getString(R.string.login_error_incorrect_token));
break;
case CODE_EXPIRED_TOKEN:
b.loginTokenLayout.setError(getString(R.string.login_error_expired_token));
break;
case CODE_INVALID_SYMBOL:
b.loginSymbolLayout.setError(getString(R.string.login_error_incorrect_symbol));
break;
case CODE_INVALID_PIN:
if (!"?".equals(error.errorText)) {
b.loginPinLayout.setError(getString(R.string.login_error_incorrect_pin_format, error.errorText));
break;
}
b.loginPinLayout.setError(getString(R.string.login_error_incorrect_pin));
break;
default:
CafeBar.builder(getActivity())
.to(b.root)
.content(getString(R.string.login_error, error.asReadableString(getActivity())))
.autoDismiss(false)
.positiveText(R.string.ok)
.onPositive(CafeBar::dismiss)
.floating(true)
.swipeToDismiss(true)
.neutralText(R.string.more)
.onNeutral(cafeBar -> app.apiEdziennik.guiShowErrorDialog(getActivity(), error, R.string.error_details))
.negativeText(R.string.report)
.onNegative((cafeBar -> app.apiEdziennik.guiReportError(getActivity(), error, null)))
.show();
break;
}
LoginActivity.error = null;
}
}, 100);
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginVulcanHelpFragment, null, LoginActivity.navOptions));
b.backButton.setOnClickListener((v) -> nav.navigateUp());
b.loginQrScan.setImageDrawable(new IconicsDrawable(getActivity()).icon(CommunityMaterial.Icon2.cmd_qrcode).color(IconicsColor.colorInt(Color.BLACK)).size(IconicsSize.dp(72)));
b.loginQrScan.setOnClickListener((v -> {
QrScannerActivity.resultHandler = result -> {
try {
String qr = result.getText();
String data = Utils.VulcanQrEncryptionUtils.decode(qr);
Matcher matcher = Pattern.compile("CERT#https?://.+?/([A-z]+)/mobile-api#([A-z0-9]+)#ENDCERT").matcher(data);
if (matcher.find()) {
b.loginToken.setText(matcher.group(2));
b.loginSymbol.setText(matcher.group(1));
if(b.loginPin.requestFocus()) {
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
}
else {
}
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (ShortBufferException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
};
startActivity(new Intent(getContext(), QrScannerActivity.class));
}));
b.loginButton.setOnClickListener((v) -> {
boolean errors = false;
b.loginTokenLayout.setError(null);
b.loginSymbolLayout.setError(null);
b.loginPinLayout.setError(null);
Editable tokenEditable = b.loginToken.getText();
Editable symbolEditable = b.loginSymbol.getText();
Editable pinEditable = b.loginPin.getText();
if (tokenEditable == null || tokenEditable.length() == 0) {
b.loginTokenLayout.setError(getString(R.string.login_error_no_token));
errors = true;
}
if (symbolEditable == null || symbolEditable.length() == 0) {
b.loginSymbolLayout.setError(getString(R.string.login_error_no_symbol));
errors = true;
}
if (pinEditable == null || pinEditable.length() == 0) {
b.loginPinLayout.setError(getString(R.string.login_error_no_pin));
errors = true;
}
if (errors)
return;
errors = false;
String token = tokenEditable.toString().toUpperCase();
String symbol = symbolEditable.toString().toLowerCase();
String pin = pinEditable.toString();
b.loginToken.setText(token);
b.loginSymbol.setText(symbol);
b.loginPin.setText(pin);
if (!token.matches("[A-Z0-9]{5,12}")) {
b.loginTokenLayout.setError(getString(R.string.login_error_incorrect_token));
errors = true;
}
if (!symbol.matches("[a-z0-9]+")) {
b.loginSymbolLayout.setError(getString(R.string.login_error_incorrect_symbol));
errors = true;
}
if (!pin.matches("[a-z0-9_]+")) {
b.loginPinLayout.setError(getString(R.string.login_error_incorrect_pin));
errors = true;
}
if (errors)
return;
errors = false;
Bundle args = new Bundle();
args.putInt("loginType", LOGIN_TYPE_VULCAN);
args.putString("deviceToken", token);
args.putString("deviceSymbol", symbol);
args.putString("devicePin", pin);
nav.navigate(R.id.loginProgressFragment, args, LoginActivity.navOptions);
});
}// narysowac raz dwa trzy cztery wyresy funkcji ktore sa tak dzielone
// nire wnikac w szkczegoly jak dzialaja
// takie same sa funckje sinus comisinus
//
}

View File

@ -0,0 +1,48 @@
package pl.szczodrzynski.edziennik.ui.modules.login;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentLoginVulcanHelpBinding;
public class LoginVulcanHelpFragment extends Fragment {
private App app;
private NavController nav;
private FragmentLoginVulcanHelpBinding b;
private static final String TAG = "LoginVulcanHelp";
public LoginVulcanHelpFragment() { }
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (getActivity() != null) {
app = (App) getActivity().getApplicationContext();
nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
}
else {
return null;
}
b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_vulcan_help, container, false);
return b.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
assert getContext() != null;
assert getActivity() != null;
b.backButton.setOnClickListener((v) -> nav.navigateUp());
}
}

View File

@ -0,0 +1,176 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.graphics.Typeface;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.MessagesItemBinding;
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_DRAFT;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_SENT;
public class MessagesAdapter extends RecyclerView.Adapter<MessagesAdapter.ViewHolder> {
private App app;
public List<MessageFull> messageList = new ArrayList<>();
private AdapterView.OnItemClickListener onItemClickListener;
public MessagesAdapter(App app, AdapterView.OnItemClickListener onItemClickListener) {
this.app = app;
this.onItemClickListener = onItemClickListener;
}
void setData(List<MessageFull> messageList) {
this.messageList = messageList;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(DataBindingUtil.inflate(inflater, R.layout.messages_item, parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
MessagesItemBinding b = holder.b;
MessageFull message = messageList.get(position);
b.getRoot().setOnClickListener((v -> {
onItemClickListener.onItemClick(null, v, position, position);
}));
ViewCompat.setTransitionName(b.getRoot(), String.valueOf(message.id));
/*if (message.type == TYPE_RECEIVED) {
b.messageSender.setText(message.senderFullName);
}
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
StringBuilder senderText = new StringBuilder();
boolean first = true;
for (MessageRecipientFull recipient: message.recipients) {
if (!first) {
senderText.append(", ");
}
first = false;
senderText.append(recipient.fullName);
}
b.messageSender.setText(senderText.toString());
}*/
b.messageSubject.setText(message.subject);
b.messageDate.setText(Date.fromMillis(message.addedDate).getFormattedStringShort());
b.messageAttachmentImage.setVisibility(message.hasAttachments() ? View.VISIBLE : View.GONE);
try {
b.messageBody.setText(
Html.fromHtml(
message.body == null ? "" : message
.body
.substring(0, Math.min(message.body.length(), 200))
.replaceAll("\\[META:[A-z0-9]+;[0-9-]+]", "")
)
);
}
catch (Exception e) {
// TODO ???
}
if (message.type == TYPE_SENT || message.type == TYPE_DRAFT || message.seen) {
b.messageSender.setTextAppearance(b.messageSender.getContext(), R.style.NavView_TextView_Small);
b.messageSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.messageSubject.setTextAppearance(b.messageSubject.getContext(), R.style.NavView_TextView_Small);
b.messageSubject.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
b.messageDate.setTextAppearance(b.messageDate.getContext(), R.style.NavView_TextView_Small);
b.messageDate.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
}
else {
b.messageSender.setTextAppearance(b.messageSender.getContext(), R.style.NavView_TextView);
b.messageSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
b.messageSubject.setTextAppearance(b.messageSubject.getContext(), R.style.NavView_TextView);
b.messageSubject.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
b.messageDate.setTextAppearance(b.messageDate.getContext(), R.style.NavView_TextView);
b.messageDate.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
}
/*if (message.type == TYPE_RECEIVED) {
if (app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(message.senderId)) {
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+message.senderId+".jpg");
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
}
else {
b.messageProfileImage.setImageDrawable(null);
int color = Colors.stringToMaterialColor(message.senderFullName);
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
if (message.senderFullName == null) {
b.messageProfileName.setText("N");
} else {
String[] nameParts = message.senderFullName.split(" ");
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
}
}
}
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
MessageRecipientFull recipient = message.recipients.get(0);
if (message.recipients.size() == 1 && app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(recipient.id)) {
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+recipient.id+".jpg");
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
}
else {
b.messageProfileImage.setImageDrawable(null);
int color = Colors.stringToMaterialColor(recipient.fullName);
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
if (recipient.fullName == null) {
b.messageProfileName.setText("N");
} else {
String[] nameParts = recipient.fullName.split(" ");
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
}
}
}*/
MessagesUtils.MessageInfo messageInfo = MessagesUtils.getMessageInfo(app, message, 48, 24, 18, 12);
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage);
b.messageSender.setText(messageInfo.profileName);
}
@Override
public int getItemCount() {
return messageList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
MessagesItemBinding b;
public ViewHolder(MessagesItemBinding b) {
super(b.getRoot());
this.b = b;
}
}
}

View File

@ -0,0 +1,209 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.databinding.DataBindingUtil;
import com.afollestad.materialdialogs.MaterialDialog;
import com.hootsuite.nachos.ChipConfiguration;
import com.hootsuite.nachos.NachoTextView;
import com.hootsuite.nachos.chip.Chip;
import com.hootsuite.nachos.chip.ChipInfo;
import com.hootsuite.nachos.chip.ChipSpan;
import com.hootsuite.nachos.chip.ChipSpanChipCreator;
import com.hootsuite.nachos.tokenizer.SpanChipTokenizer;
import com.hootsuite.nachos.validator.IllegalCharacterIdentifier;
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.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.api.Edziennik;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.MessagesComposeActivityBinding;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.datamodels.Teacher;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Themes;
public class MessagesComposeActivity extends AppCompatActivity {
private static final String TAG = "MessageCompose";
private App app;
private MessagesComposeActivityBinding b;
private List<Teacher> teachers = new ArrayList<>();
private ActionBar actionBar;
private MessagesComposeInfo composeInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (App)getApplication();
setTheme(Themes.INSTANCE.getAppTheme());
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.messages_compose_activity, null, false);
setContentView(b.getRoot());
composeInfo = Edziennik.getApi(app, app.profile.getLoginStoreType()).getComposeInfo(app.profile);
Toolbar toolbar = b.toolbar;
setSupportActionBar(toolbar);
actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(R.string.messages_compose_title);
}
List<Teacher> categories = new ArrayList<>();
for (int i = 0; i < 11; i++) {
categories.add(new Teacher(-1, -1*i, Teacher.typeString(this, i), ""));
}
Edziennik.getApi(app, app.profile.getLoginStoreType()).getRecipientList(this, new SyncCallback() {
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
@Override public void onSuccess(Context activityContext, ProfileFull profileFull) { }
@Override public void onProgress(int progressStep) { }
@Override public void onActionStarted(int stringResId) { }
@Override
public void onError(Context activityContext, AppError error) {
new Handler(activityContext.getMainLooper()).post(() -> {
app.apiEdziennik.guiShowErrorDialog(MessagesComposeActivity.this, error, R.string.messages_recipient_list_download_error);
});
}
}, app.profile, teacherList -> {
teachers.clear();
for (Teacher teacher: teacherList) {
if (teacher.loginId != null)
teachers.add(teacher);
}
teachers.addAll(categories);
MessagesComposeSuggestionAdapter adapter = new MessagesComposeSuggestionAdapter(this, teachers);
//ArrayAdapter<Teacher> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, teachers);
b.nachoTextView.setAdapter(adapter);
});
/*app.db.teacherDao().getAllTeachers(App.profileId).observe(this, teachers -> {
});*/
/*int[][] states = new int[][] {
new int[] {}
};
int[] colors = new int[] {
getResources().getColor(ThemeUtils.getChipColorRes())
};*/
//ColorStateList chipStateList = new ColorStateList(states, colors);
b.nachoTextView.setChipTokenizer(new SpanChipTokenizer<>(this, new ChipSpanChipCreator() {
@Override
public ChipSpan createChip(@NonNull Context context, @NonNull CharSequence text, Object data) {
Teacher teacher = (Teacher) data;
if (teacher.id <= 0) {
int type = (int) (teacher.id * -1);
List<Teacher> category = new ArrayList<>();
List<String> categoryNames = new ArrayList<>();
for (Teacher teacher1: teachers) {
if (teacher1.isType(type)) {
category.add(teacher1);
categoryNames.add(teacher1.getFullName());
}
}
new MaterialDialog.Builder(MessagesComposeActivity.this)
.title(R.string.messages_compose_recipients_title)
.content(getString(R.string.messages_compose_recipients_text_format, Teacher.typeString(MessagesComposeActivity.this, type)))
.items(categoryNames)
.itemsCallbackMultiChoice(null, ((dialog, which, text1) -> {
List<ChipInfo> chipInfoList = new ArrayList<>();
for (int index: which) {
Teacher selected = category.get(index);
selected.image = MessagesUtils.getProfileImage(48, 24, 16, 12, 1, selected.getFullName());
chipInfoList.add(new ChipInfo(selected.getFullName(), selected));
}
b.nachoTextView.addTextWithChips(chipInfoList);
return true;
}))
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.show();
return null;
}
ChipSpan chipSpan = new ChipSpan(context, text, new BitmapDrawable(context.getResources(), teacher.image), teacher);
chipSpan.setIconBackgroundColor(Colors.stringToMaterialColor(teacher.getFullName()));
return chipSpan;
}
@Override
public void configureChip(@NonNull ChipSpan chip, @NonNull ChipConfiguration chipConfiguration) {
super.configureChip(chip, chipConfiguration);
//chip.setBackgroundColor(chipStateList);
chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(MessagesComposeActivity.this));
}
}, ChipSpan.class));
b.nachoTextView.setIllegalCharacterIdentifier(new IllegalCharacterIdentifier() {
@Override
public boolean isCharacterIllegal(Character c) {
return c.toString().matches("[\\n;:_ ]");
}
});
//b.nachoTextView.addChipTerminator('\n', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_ALL);
//b.nachoTextView.addChipTerminator(' ', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_TO_TERMINATOR);
//b.nachoTextView.addChipTerminator(';', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_CURRENT_TOKEN);
//b.nachoTextView.setNachoValidator(new ChipifyingNachoValidator());
//b.nachoTextView.enableEditChipOnTouch(false, false);
//b.nachoTextView.disableEditChipOnTouch();
b.nachoTextView.setOnChipClickListener(new NachoTextView.OnChipClickListener() {
@Override
public void onChipClick(Chip chip, MotionEvent motionEvent) {
Toast.makeText(app, "onChipClick: " + chip.getText(), Toast.LENGTH_SHORT).show();
}
});
b.nachoTextView.setOnChipRemoveListener(new NachoTextView.OnChipRemoveListener() {
@Override
public void onChipRemove(Chip chip) {
Log.d(TAG, "onChipRemoved: " + chip.getText());
b.nachoTextView.setSelection(b.nachoTextView.getText().length());
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_compose, menu);
menu.findItem(R.id.action_send).setIcon(
new IconicsDrawable(this, CommunityMaterial.Icon2.cmd_send)
.actionBar()
.color(IconicsColor.colorInt(Color.WHITE))
.size(IconicsSize.dp(20))
);
menu.findItem(R.id.action_attachment).setIcon(
new IconicsDrawable(this, CommunityMaterial.Icon.cmd_attachment)
.actionBar()
.color(IconicsColor.colorInt(Color.WHITE))
.size(IconicsSize.dp(20))
);
menu.findItem(R.id.action_attachment).setVisible(composeInfo.maxAttachmentNumber != 0);
return true;
}
}

View File

@ -0,0 +1,28 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
public class MessagesComposeInfo {
/**
* 0 means no attachments.
* -1 means unlimited number.
*/
public int maxAttachmentNumber = 0;
/**
* -1 means unlimited size.
*/
public long attachmentSizeLimit = 0;
/**
* -1 means unlimited length.
*/
public int maxSubjectLength = -1;
/**
* -1 means unlimited length.
*/
public int maxBodyLength = -1;
public MessagesComposeInfo(int maxAttachmentNumber, long attachmentSizeLimit, int maxSubjectLength, int maxBodyLength) {
this.maxAttachmentNumber = maxAttachmentNumber;
this.attachmentSizeLimit = attachmentSizeLimit;
this.maxSubjectLength = maxSubjectLength;
this.maxBodyLength = maxBodyLength;
}
}

View File

@ -0,0 +1,191 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.content.Context;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.Teacher;
public class MessagesComposeSuggestionAdapter extends ArrayAdapter<Teacher> {
private Context context;
private List<Teacher> teacherList;
private ArrayList<Teacher> originalList = null;
private ArrayFilter mFilter;
private final Object mLock = new Object();
MessagesComposeSuggestionAdapter(@NonNull Context context, List<Teacher> teacherList) {
super(context, 0, teacherList);
this.context = context;
this.teacherList = teacherList;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View listItem = convertView;
if(listItem == null)
listItem = LayoutInflater.from(context).inflate(R.layout.messages_compose_suggestion_item, parent, false);
Teacher teacher = teacherList.get(position);
TextView name = listItem.findViewById(R.id.name);
TextView type = listItem.findViewById(R.id.type);
ImageView image = listItem.findViewById(R.id.image);
teacher.image = MessagesUtils.getProfileImage(48, 24, 16, 12, 1, teacher.getFullName());
if (teacher.id <= 0) {
name.setText(Teacher.typeString(context, (int) (teacher.id * -1)));
type.setText(R.string.teachers_browse_category);
image.setImageBitmap(null);
}
else {
if (teacher.displayName == null)
name.setText(teacher.getFullName());
else
name.setText(Html.fromHtml(teacher.displayName));
type.setText(teacher.getType(context));
image.setImageBitmap(teacher.image);
}
return listItem;
}
@Override
public int getCount() {
return teacherList.size();
}
@Nullable
@Override
public Teacher getItem(int position) {
return teacherList.get(position);
}
@Override
public int getPosition(@Nullable Teacher item) {
return teacherList.indexOf(item);
}
@NonNull
@Override
public Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
return mFilter;
}
private class TeacherWeighted extends Teacher {
public int weight;
public TeacherWeighted(Teacher teacher, int weight) {
super(teacher.profileId, teacher.id, teacher.name, teacher.surname, teacher.loginId);
this.weight = weight;
this.image = teacher.image;
this.type = teacher.type;
this.typeDescription = teacher.typeDescription;
}
@NonNull
@Override
public String toString() {
return getFullName();
}
}
private Comparator<? super Teacher> comparator = (o1, o2) -> ((TeacherWeighted) o1).weight - ((TeacherWeighted) o2).weight;
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
final FilterResults results = new FilterResults();
if (originalList == null) {
synchronized (mLock) {
originalList = new ArrayList<>(teacherList);
}
}
if (prefix == null || prefix.length() == 0) {
ArrayList<Teacher> list;
synchronized (mLock) {
list = new ArrayList<>(originalList);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = prefix.toString().toLowerCase();
ArrayList<Teacher> values;
synchronized (mLock) {
values = new ArrayList<>(originalList);
}
int count = values.size();
ArrayList<Teacher> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
Teacher teacher = values.get(i);
String teacherFullName = teacher.getFullName().toLowerCase();
teacher.displayName = teacherFullName.replace(prefixString, "<b>"+prefixString+"</b>");
// First match against the whole, non-splitted value
boolean found = false;
if (teacherFullName.startsWith(prefixString)) {
newValues.add(new TeacherWeighted(teacher, 1));
found = true;
} else {
// check if prefix matches any of the words
String[] words = teacherFullName.split(" ");
for (String word : words) {
if (word.startsWith(prefixString)) {
newValues.add(new TeacherWeighted(teacher, 2));
found = true;
break;
}
}
}
// finally check if the prefix matches any part of the name
if (!found && teacherFullName.contains(prefixString)) {
newValues.add(new TeacherWeighted(teacher, 3));
}
}
Collections.sort(newValues, comparator);
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
teacherList = (List<Teacher>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}

View File

@ -0,0 +1,676 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.material.chip.Chip;
import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import com.theartofdev.edmodo.cropper.CropImage;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import im.wangchao.mhttp.Response;
import im.wangchao.mhttp.callback.FileCallbackHandler;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.api.Edziennik;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.MessagesDetailsBinding;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
import pl.szczodrzynski.edziennik.datamodels.MessageRecipientFull;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import static android.app.Activity.RESULT_OK;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static pl.szczodrzynski.edziennik.utils.Utils.getResizedBitmap;
import static pl.szczodrzynski.edziennik.utils.Utils.getStringFromFile;
import static pl.szczodrzynski.edziennik.utils.Utils.readableFileSize;
public class MessagesDetailsFragment extends Fragment {
private long messageId = -1;
private App app = null;
private MainActivity activity = null;
private MessagesDetailsBinding b = null;
private MessageFull message;
private List<Attachment> attachmentList = new ArrayList<>();
@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.messages_details, container, false);
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;
if (getArguments() != null)
messageId = getArguments().getLong("messageId", -1);
b.messageContent.setVisibility(View.GONE);
if (messageId != -1) {
AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
MessageFull messageRaw = app.db.messageDao().getById(App.profileId, messageId);
Edziennik.getApi(app, app.profile.getLoginStoreType()).getMessage(activity, new SyncCallback() {
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
@Override public void onSuccess(Context activityContext, ProfileFull profileFull) { }
@Override public void onProgress(int progressStep) { }
@Override public void onActionStarted(int stringResId) { }
@Override
public void onError(Context activityContext, AppError error) {
new Handler(activityContext.getMainLooper()).post(() -> {
app.apiEdziennik.guiShowErrorDialog(activity, error, R.string.messages_download_error);
});
}
}, app.profile, messageRaw, messageFull -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
message = messageFull;
if (message.body == null) {
return;
}
b.messageBody.setText(Html.fromHtml(message.body.replaceAll("\\[META:[A-z0-9]+;[0-9-]+]", "")));
b.progress.setVisibility(View.GONE);
Anim.fadeIn(b.messageContent, 200, null);
MessagesFragment.Companion.setPageSelection(Math.min(message.type, 1));
MessagesUtils.MessageInfo messageInfo = MessagesUtils.getMessageInfo(app, message, 40, 20, 14, 10);
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage);
b.messageSender.setText(messageInfo.profileName);
b.messageSubject.setText(message.subject);
b.messageDate.setText(getString(R.string.messages_date_time_format, Date.fromMillis(message.addedDate).getFormattedStringShort(), Time.fromMillis(message.addedDate).getStringHM()));
StringBuilder messageRecipients = new StringBuilder("<ul>");
for (MessageRecipientFull recipient: message.recipients) {
if (recipient.readDate == -1) messageRecipients.append(getString(
R.string.messages_recipients_list_unknown_state_format,
recipient.fullName
));
else if (recipient.readDate == 0) messageRecipients.append(getString(
R.string.messages_recipients_list_unread_format,
recipient.fullName
));
else if (recipient.readDate == 1) messageRecipients.append(getString(
R.string.messages_recipients_list_read_unknown_date_format,
recipient.fullName
));
else messageRecipients.append(getString(
R.string.messages_recipients_list_read_format,
recipient.fullName,
Date.fromMillis(recipient.readDate).getFormattedString(),
Time.fromMillis(recipient.readDate).getStringHM()
));
}
messageRecipients.append("</ul>");
b.messageRecipients.setText(Html.fromHtml(messageRecipients.toString()));
if (message.attachmentIds != null) {
// there are some attachments: attachmentIds, attachmentNames, attachmentSizes
ViewGroup insertPoint = b.messageAttachments;
FrameLayout.LayoutParams chipLayoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
chipLayoutParams.setMargins(0, Utils.dpToPx(8), 0, Utils.dpToPx(8));
FrameLayout.LayoutParams progressLayoutParams = new FrameLayout.LayoutParams(Utils.dpToPx(18), Utils.dpToPx(18));
progressLayoutParams.setMargins(Utils.dpToPx(8), 0, Utils.dpToPx(8), 0);
progressLayoutParams.gravity = END | CENTER_VERTICAL;
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
int attachmentIndex = 0;
for (String attachmentName: message.attachmentNames) {
long messageId = message.id;
long attachmentId = message.attachmentIds.get(attachmentIndex);
long attachmentSize = message.attachmentSizes.get(attachmentIndex);
// create the parent
FrameLayout attachmentLayout = new FrameLayout(b.getRoot().getContext());
Chip attachmentChip = new Chip(attachmentLayout.getContext());
//attachmentChip.setChipBackgroundColorResource(ThemeUtils.getChipColorRes());
attachmentChip.setLayoutParams(chipLayoutParams);
attachmentChip.setHeight(Utils.dpToPx(40));
// show the file size or not
if (attachmentSize == -1)
attachmentChip.setText(getString(R.string.messages_attachment_no_size_format, attachmentName));
else
attachmentChip.setText(getString(R.string.messages_attachment_format, attachmentName, readableFileSize(attachmentSize)));
attachmentChip.setEllipsize(TextUtils.TruncateAt.MIDDLE);
// create an icon for the attachment
IIcon icon = CommunityMaterial.Icon.cmd_file;
switch (Utils.getExtensionFromFileName(attachmentName)) {
case "txt":
icon = CommunityMaterial.Icon.cmd_file_document;
break;
case "doc":
case "docx":
case "odt":
case "rtf":
icon = CommunityMaterial.Icon.cmd_file_word;
break;
case "xls":
case "xlsx":
case "ods":
icon = CommunityMaterial.Icon.cmd_file_excel;
break;
case "ppt":
case "pptx":
case "odp":
icon = CommunityMaterial.Icon.cmd_file_powerpoint;
break;
case "pdf":
icon = CommunityMaterial.Icon.cmd_file_pdf;
break;
case "mp3":
case "wav":
case "aac":
icon = CommunityMaterial.Icon.cmd_file_music;
break;
case "mp4":
case "avi":
case "3gp":
case "mkv":
case "flv":
icon = CommunityMaterial.Icon.cmd_file_video;
break;
case "jpg":
case "jpeg":
case "png":
case "bmp":
case "gif":
icon = CommunityMaterial.Icon.cmd_file_image;
break;
case "zip":
case "rar":
case "tar":
case "7z":
icon = CommunityMaterial.Icon.cmd_file_lock;
break;
}
attachmentChip.setChipIcon(new IconicsDrawable(activity).color(IconicsColor.colorRes(R.color.colorPrimary)).icon(icon).size(IconicsSize.dp(26)));
attachmentChip.setCloseIcon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_check).size(IconicsSize.dp(18)).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorPrimary))));
attachmentChip.setCloseIconVisible(false);
// set the object's index in the attachmentList as the tag
attachmentChip.setTag(attachmentIndex);
attachmentChip.setOnClickListener(v -> {
if (v.getTag() instanceof Integer) {
downloadAttachment((Integer) v.getTag());
}
});
attachmentLayout.addView(attachmentChip);
ProgressBar attachmentProgress = new ProgressBar(attachmentLayout.getContext());
attachmentProgress.setLayoutParams(progressLayoutParams);
attachmentProgress.setVisibility(View.GONE);
attachmentLayout.addView(attachmentProgress);
insertPoint.addView(attachmentLayout);
// create an object and add to the list
Attachment a = new Attachment(App.profileId, messageId, attachmentId, attachmentName, attachmentSize, attachmentLayout, attachmentChip, attachmentProgress);
attachmentList.add(a);
// check if the file is already downloaded. Show the check icon if necessary and set `downloaded` to true.
checkAttachment(a);
attachmentIndex++;
}
}
else {
// no attachments found
b.messageAttachmentsTitle.setVisibility(View.GONE);
}
});
});
}
// click to expand subject and sender
b.messageSubject.setOnClickListener(v -> {
if (b.messageSubject.getMaxLines() == 2) {
b.messageSubject.setMaxLines(30);
}
else {
b.messageSubject.setMaxLines(2);
}
});
b.messageSender.setOnClickListener(v -> {
if (b.messageSender.getMaxLines() == 3) {
b.messageSender.setMaxLines(30);
}
else {
b.messageSender.setMaxLines(3);
}
});
// message close button
b.messageClose.setImageDrawable(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_window_close).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorSecondary))).size(IconicsSize.dp(12)));
b.messageClose.setOnClickListener(v -> {
activity.navigateUp();
});
// enter, exit transitions
//setExitTransition(new Fade());
/*View content = b.getRoot();
content.setAlpha(0f);
ValueAnimator animator = ObjectAnimator.ofFloat(content, View.ALPHA, 0f, 1f);
animator.setStartDelay(50);
animator.setDuration(150);
animator.start();*/
}
private void checkAttachment(Attachment a) {
File storageDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu");
storageDir.mkdirs();
File attachmentDataFile = new File(storageDir, "."+a.profileId+"_"+a.messageId+"_"+a.attachmentId);
if (attachmentDataFile.exists()) {
try {
String attachmentFileName = getStringFromFile(attachmentDataFile);
File attachmentFile = new File(attachmentFileName);
if (attachmentFile.exists()) {
a.downloaded = attachmentFileName;
a.chip.setCloseIconVisible(true);
}
} catch (Exception e) {
e.printStackTrace();
app.apiEdziennik.guiReportException(activity, 355, e);
}
}
}
private void downloadAttachment(int index) {
Attachment a = attachmentList.get(index);
if (a.downloaded != null) {
Utils.openFile(activity, new File(a.downloaded));
return;
}
a.chip.setEnabled(false);
a.chip.setTextColor(Themes.INSTANCE.getSecondaryTextColor(activity));
a.progressBar.setVisibility(View.VISIBLE);
File storageDir = Utils.getStorageDir();
Edziennik.getApi(app, app.profile.getLoginStoreType()).getAttachment(activity, new SyncCallback() {
@Override
public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) {
}
@Override
public void onSuccess(Context activityContext, ProfileFull profileFull) {
}
@Override
public void onError(Context activityContext, AppError error) {
new Handler(activityContext.getMainLooper()).post(() -> {
a.progressBar.setVisibility(View.GONE);
a.chip.setEnabled(true);
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
a.chip.setCloseIconVisible(false);
app.apiEdziennik.guiShowErrorDialog(activity, error, R.string.messages_attachment_cannot_download);
});
}
@Override
public void onProgress(int progressStep) {
}
@Override
public void onActionStarted(int stringResId) {
}
}, app.profile, message, a.attachmentId, builder ->
builder.callbackThreadMode(im.wangchao.mhttp.ThreadMode.SENDING)
.callback(new FileCallbackHandler(new File(storageDir, a.attachmentName)) {
@Override
public void onSuccess(File file, Response response) {
AttachmentEvent event = new AttachmentEvent();
event.profileId = a.profileId;
event.messageId = a.messageId;
event.attachmentId = a.attachmentId;
event.eventType = AttachmentEvent.TYPE_FINISHED;
event.fileName = file.getAbsolutePath();
try {
File attachmentDataFile = new File(Utils.getStorageDir(), "."+event.profileId+"_"+event.messageId+"_"+event.attachmentId);
Utils.writeStringToFile(attachmentDataFile, event.fileName);
} catch (IOException e) {
event.eventType = AttachmentEvent.TYPE_ERROR;
event.exception = e;
}
finally {
EventBus.getDefault().post(event);
}
}
@Override
public void onFailure(Response response, Throwable throwable) {
AttachmentEvent event = new AttachmentEvent();
event.profileId = a.profileId;
event.messageId = a.messageId;
event.attachmentId = a.attachmentId;
event.eventType = AttachmentEvent.TYPE_ERROR;
event.exception = new Exception(throwable);
EventBus.getDefault().post(event);
}
@Override
public void onProgress(long bytesWritten, long bytesTotal) {
AttachmentEvent event = new AttachmentEvent();
event.profileId = a.profileId;
event.messageId = a.messageId;
event.attachmentId = a.attachmentId;
event.eventType = AttachmentEvent.TYPE_PROGRESS;
event.progress = (float)bytesWritten / (float)bytesTotal * 100.0f;
EventBus.getDefault().post(event);
}
})
.build()
.enqueue());
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void downloadAttachmentEvent(AttachmentEvent event) {
try {
for (Attachment a: attachmentList) {
if (a.profileId == event.profileId
&& a.messageId == event.messageId
&& a.attachmentId == event.attachmentId) {
if (event.eventType == AttachmentEvent.TYPE_PROGRESS) {
// show downloading progress
a.chip.setText(getString(R.string.messages_attachment_downloading_format, a.attachmentName, Math.round(event.progress)));
}
else if (event.eventType == AttachmentEvent.TYPE_FINISHED) {
// save the downloaded file name
a.downloaded = event.fileName;
// set the correct name (and size)
if (a.attachmentSize == -1)
a.chip.setText(getString(R.string.messages_attachment_no_size_format, a.attachmentName));
else
a.chip.setText(getString(R.string.messages_attachment_format, a.attachmentName, readableFileSize(a.attachmentSize)));
// hide the progress bar and show a tick icon
a.progressBar.setVisibility(View.GONE);
a.chip.setEnabled(true);
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
a.chip.setCloseIconVisible(true);
// open the file
Utils.openFile(activity, new File(a.downloaded));
}
else if (event.eventType == AttachmentEvent.TYPE_ERROR) {
a.progressBar.setVisibility(View.GONE);
a.chip.setEnabled(true);
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
a.chip.setCloseIconVisible(false);
new MaterialDialog.Builder(activity)
.title(R.string.messages_attachment_cannot_download)
.content(R.string.messages_attachment_cannot_download_text)
.positiveText(R.string.ok)
.neutralColor(R.string.report)
.onNeutral((dialog, which) -> {
app.apiEdziennik.guiReportException(activity, 433, event.exception);
})
.show();
}
}
}
}
catch (Exception e) {
app.apiEdziennik.guiReportException(activity, 425, e);
}
}
private class Attachment {
int profileId;
long messageId;
long attachmentId;
String attachmentName;
long attachmentSize;
FrameLayout parent;
Chip chip;
ProgressBar progressBar;
/**
* An absolute path of the downloaded file. {@code null} if not downloaded yet.
*/
String downloaded = null;
public Attachment(int profileId, long messageId, long attachmentId, String attachmentName, long attachmentSize, FrameLayout parent, Chip chip, ProgressBar progressBar) {
this.profileId = profileId;
this.messageId = messageId;
this.attachmentId = attachmentId;
this.attachmentName = attachmentName;
this.attachmentSize = attachmentSize;
this.parent = parent;
this.chip = chip;
this.progressBar = progressBar;
}
}
public static class AttachmentEvent {
public int profileId;
public long messageId;
public long attachmentId;
public static final int TYPE_PROGRESS = 0;
public static final int TYPE_FINISHED = 1;
public static final int TYPE_ERROR = 2;
public int eventType = TYPE_PROGRESS;
public float progress = 0.0f;
public String fileName = null;
public Exception exception = null;
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
CropImage.ActivityResult result = CropImage.getActivityResult(data);
Uri uri = result.getUri();
File photoFile = new File(uri.getPath());
File destFile = new File(getContext().getFilesDir(),"teacher_"+message.senderId+".jpg");
if (destFile.exists()) {
destFile.delete();
destFile = new File(getContext().getFilesDir(),"teacher_"+message.senderId+".jpg");
}
if (result.getCropRect().width() > 512) {
Bitmap bigImage = BitmapFactory.decodeFile(uri.getPath());
Bitmap smallImage = getResizedBitmap(bigImage, 512, 512);
try (FileOutputStream out = new FileOutputStream(destFile)) {
smallImage.compress(Bitmap.CompressFormat.JPEG, 80, out); // bmp is your Bitmap instance
// PNG is a lossless format, the compression factor (100) is ignored
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(destFile.getAbsolutePath());
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
if (app.appConfig.teacherImages == null) {
app.appConfig.teacherImages = new HashMap<>();
}
app.appConfig.teacherImages.put(message.senderId, true);
app.saveConfig("teacherImages");
if (photoFile.exists()) {
photoFile.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
else {
if (photoFile.renameTo(destFile)) {
// success
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(destFile.getAbsolutePath());
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
if (app.appConfig.teacherImages == null) {
app.appConfig.teacherImages = new HashMap<>();
}
app.appConfig.teacherImages.put(message.senderId, true);
app.saveConfig("teacherImages");
}
else {
// not this time
Toast.makeText(app, R.string.error_occured, Toast.LENGTH_LONG).show();
}
}
}
}
}
/*if (message.type == TYPE_RECEIVED) {
b.messageSender.setText(message.senderFullName);
}
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
StringBuilder senderText = new StringBuilder();
boolean first = true;
for (MessageRecipientFull recipient: message.recipients) {
if (!first) {
senderText.append(", ");
}
first = false;
senderText.append(recipient.fullName);
}
b.messageSender.setText(senderText.toString());
}
if (message.type == TYPE_RECEIVED) {
if (app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(message.senderId)) {
Bitmap profileImage;
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+message.senderId+".jpg");
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
roundDrawable.setCircular(true);
b.messageProfileImage.setImageDrawable(roundDrawable);
}
else {
int color = Colors.stringToMaterialColor(message.senderFullName);
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
if (message.senderFullName != null) {
String[] nameParts = message.senderFullName.split(" ");
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
}
else {
b.messageProfileName.setText("N");
}
}
View.OnClickListener onClickListener = v -> new MaterialDialog.Builder(activity)
.title(R.string.settings_profile_change_title)
.items(
getString(R.string.settings_profile_change_image),
getString(R.string.settings_profile_remove_image)
)
.itemsCallback((dialog, itemView, position, text) -> {
switch (position) {
case 0:
CropImage.activity()
.setAspectRatio(1, 1)
//.setMaxCropResultSize(512, 512)
.setCropShape(CropImageView.CropShape.OVAL)
.setGuidelines(CropImageView.Guidelines.ON)
.start(activity, MessagesDetailsFragment.this);
break;
case 1:
if (app.appConfig.teacherImages != null) {
app.appConfig.teacherImages.remove(message.senderId);
app.saveConfig("teacherImages");
}
break;
}
})
.negativeText(R.string.cancel)
.show();
b.messageSender.setOnClickListener(onClickListener);
b.messageProfileBackground.setOnClickListener(onClickListener);
}*/

View File

@ -0,0 +1,106 @@
package pl.szczodrzynski.edziennik.ui.modules.messages
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding
import pl.szczodrzynski.edziennik.datamodels.Message
import pl.szczodrzynski.edziennik.utils.Themes
import java.util.*
class MessagesFragment : Fragment() {
companion object {
var pageSelection = 0
}
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentMessagesBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentMessagesBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
return
val messageId = arguments?.getLong("messageId", -1L) ?: -1L
if (messageId != -1L) {
val args = Bundle()
args.putLong("messageId", messageId)
arguments!!.remove("messageId")
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args)
return
}
b.viewPager.adapter = Adapter(childFragmentManager).also { adapter ->
adapter.addFragment(MessagesListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("messageType", Message.TYPE_RECEIVED)
}
}, getString(R.string.menu_messages_inbox))
adapter.addFragment(MessagesListFragment().also { fragment ->
fragment.arguments = Bundle().also { args ->
args.putInt("messageType", Message.TYPE_SENT)
}
}, getString(R.string.menu_messages_sent))
}
b.viewPager.currentItem = pageSelection
b.viewPager.clearOnPageChangeListeners()
b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
pageSelection = position
}
})
b.tabLayout.setupWithViewPager(b.viewPager)
}
internal class Adapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
private val mFragmentList = ArrayList<Fragment>()
private val mFragmentTitleList = ArrayList<String>()
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getCount(): Int {
return mFragmentList.size
}
fun addFragment(fragment: Fragment, title: String) {
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}
override fun getPageTitle(position: Int): CharSequence? {
return mFragmentTitleList[position]
}
}
}

View File

@ -0,0 +1,355 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.content.Context;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.ArrayList;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.api.AppError;
import pl.szczodrzynski.edziennik.api.Edziennik;
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback;
import pl.szczodrzynski.edziennik.databinding.MessagesListBinding;
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
import pl.szczodrzynski.edziennik.datamodels.Message;
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
import pl.szczodrzynski.edziennik.datamodels.MessageRecipientFull;
import pl.szczodrzynski.edziennik.datamodels.Profile;
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
import pl.szczodrzynski.edziennik.utils.Themes;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_LIBRUS;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class MessagesListFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private MessagesListBinding b = null;
private Rect viewRect = new Rect();
private MessagesAdapter messagesAdapter = null;
private ViewGroup viewParent = null;
static final Interpolator transitionInterpolator = new FastOutSlowInInterpolator();
static final long TRANSITION_DURATION = 300L;
static final String TAP_POSITION = "tap_position";
private static int tapPosition = NO_POSITION;
private static int topPosition = NO_POSITION;
private static int bottomPosition = NO_POSITION;
private int messageType = Message.TYPE_RECEIVED;
@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.messages_list, container, false);
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;
long messageId = -1;
if (getArguments() != null) {
messageId = getArguments().getLong("messageId", -1);
}
if (messageId != -1) {
Bundle args = new Bundle();
args.putLong("messageId", messageId);
getArguments().remove("messageId");
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
return;
}
if (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null) {
new MaterialDialog.Builder(activity)
.title("Wiadomości w systemie Synergia")
.content("Moduł Wiadomości w aplikacji Szkolny.eu jest przeglądarką zasobów szkolnego konta Synergia. Z tego powodu, musisz wpisać swoje hasło do tego konta, aby móc korzystać z tej funkcji.")
.positiveText(R.string.ok)
.onPositive(((dialog, which) -> {
new MaterialDialog.Builder(activity)
.title("Zaloguj się")
.content(Html.fromHtml("Podaj hasło do konta Synergia z loginem <b>"+app.profile.getStudentData("accountLogin", "???")+"</b>"))
.inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD)
.input(null, null, (dialog1, input) -> {
app.profile.putStudentData("accountPassword", input.toString());
Edziennik.getApi(app, app.profile.getLoginStoreType()).syncMessages(activity, new SyncCallback() {
@Override
public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) {
}
@Override
public void onSuccess(Context activityContext, ProfileFull profileFull) {
app.profile.putStudentData("accountPassword", input.toString());
app.profileSaveFullAsync(profileFull);
((MainActivity) activityContext).recreate();
}
@Override
public void onError(Context activityContext, AppError error) {
new Handler(activityContext.getMainLooper()).post(() -> {
app.profile.removeStudentData("accountPassword");
app.profileSaveFullAsync(app.profile);
new MaterialDialog.Builder(activity)
.title(R.string.login_failed)
.content(R.string.login_failed_text)
.positiveText(R.string.ok)
.neutralText(R.string.report)
.onNeutral(((dialog2, which1) -> {
app.apiEdziennik.guiReportError(getActivity(), error, null);
}))
.show();
});
}
@Override
public void onProgress(int progressStep) {
}
@Override
public void onActionStarted(int stringResId) {
}
}, app.profile);
})
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.show();
}))
.show();
}
if (getArguments() != null) {
messageType = getArguments().getInt("messageType", Message.TYPE_RECEIVED);
}
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(messageType, b.refreshLayout);
});*/
messagesAdapter = new MessagesAdapter(app, ((parent, view1, position, id) -> {
// TODO ANIMATION
/*tapPosition = position;
topPosition = ((LinearLayoutManager) b.emailList.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
bottomPosition = ((LinearLayoutManager) b.emailList.getLayoutManager()).findLastCompletelyVisibleItemPosition();
view1.getGlobalVisibleRect(viewRect);
((Transition) MessagesListFragment.this.getExitTransition()).setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(@NonNull Transition transition) {
return viewRect;
}
});*/
Bundle args = new Bundle();
args.putLong("messageId", messagesAdapter.messageList.get(position).id);
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, args);
// KOD W WERSJI 2.7
// TODO ANIMATION
/*TransitionSet sharedElementTransition = new TransitionSet()
.addTransition(new Fade())
.addTransition(new ChangeBounds())
.addTransition(new ChangeTransform())
.addTransition(new ChangeImageTransform())
.setDuration(TRANSITION_DURATION)
.setInterpolator(transitionInterpolator);
MessagesDetailsFragment fragment = new MessagesDetailsFragment();
Bundle args = new Bundle();
args.putLong("messageId", messagesAdapter.messageList.get(position).id);
fragment.setArguments(args);
fragment.setSharedElementEnterTransition(sharedElementTransition);
fragment.setSharedElementReturnTransition(sharedElementTransition);*/
// JAKIS STARSZY KOD
/*Intent intent = new Intent(activity, MessagesDetailsActivity.class);
intent.putExtra("item_id", 1);
intent.putExtra("transition_name", ViewCompat.getTransitionName(view1));
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
activity,
view1,
getString(R.string.transition_name)
);
TransitionManager.beginDelayedTransition((ViewGroup) view1, sharedElementTransition);
setEnterTransition(sharedElementTransition);
setReturnTransition(sharedElementTransition);
setExitTransition(sharedElementTransition);
setSharedElementEnterTransition(sharedElementTransition);
setSharedElementReturnTransition(sharedElementTransition);
startActivity(intent, options.toBundle());*/
/*activity.getSupportFragmentManager()
.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.addSharedElement(view1, getString(R.string.transition_name))
.commit();*/
}));
//tapPosition = savedInstanceState != null ? savedInstanceState.getInt(TAP_POSITION, tapPosition) : tapPosition;
// May not be the best place to postpone transition. Just an example to demo how reenter transition works.
// TODO ANIMATION
//postponeEnterTransition();
viewParent = (ViewGroup) view.getParent();
b.emailList.setLayoutManager(new LinearLayoutManager(view.getContext()));
b.emailList.addItemDecoration(new DividerItemDecoration(view.getContext(), LinearLayoutManager.VERTICAL));
b.emailList.setAdapter(messagesAdapter);
if (messageType == Message.TYPE_RECEIVED) {
app.db.messageDao().getReceived(App.profileId).observe(this, messageFulls -> {
createMessageList(messageFulls);
});
}
else if (messageType == Message.TYPE_DELETED) {
app.db.messageDao().getDeleted(App.profileId).observe(this, messageFulls -> {
createMessageList(messageFulls);
});
}
else if (messageType == Message.TYPE_SENT) {
app.db.messageDao().getSent(App.profileId).observe(this, messageFulls -> {
AsyncTask.execute(() -> {
List<MessageRecipientFull> messageRecipients = app.db.messageRecipientDao().getAll(App.profileId);
List<Long> messageIds = new ArrayList<>();
for (MessageFull messageFull: messageFulls) {
messageIds.add(messageFull.id);
}
for (MessageRecipientFull messageRecipientFull: messageRecipients) {
if (messageRecipientFull.id == -1)
continue;
int index = -1;
int i = -1;
for (long id: messageIds) {
//index++;
i++;
if (id == messageRecipientFull.messageId) {
index = i;
break;
}
}
if (index >= 0) {
MessageFull messageFull = messageFulls.get(index);
if (messageFull != null) {
messageFull.addRecipient(messageRecipientFull);
}
}
}
activity.runOnUiThread(() -> {
createMessageList(messageFulls);
});
});
});
}
}
private void createMessageList(List<MessageFull> messageFulls) {
b.progressBar.setVisibility(View.GONE);
b.emailList.setVisibility(View.VISIBLE);
messagesAdapter.setData(messageFulls);
// TODO ANIMATION
/*final ViewTreeObserver observer = viewParent.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
viewParent.getViewTreeObserver().removeOnPreDrawListener(this);
if (getExitTransition() == null) {
setExitTransition(new SlideExplode().setDuration(TRANSITION_DURATION).setInterpolator(transitionInterpolator));
}
LinearLayoutManager layoutManager = (LinearLayoutManager) b.emailList.getLayoutManager();
View view2 = layoutManager != null ? layoutManager.findViewByPosition(tapPosition) : null;
if (view2 != null) {
view2.getGlobalVisibleRect(viewRect);
((Transition) getExitTransition()).setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(@NonNull Transition transition) {
return viewRect;
}
});
}
d("MessagesList", "topPosition "+topPosition);
d("MessagesList", "tapPosition "+tapPosition);
d("MessagesList", "bottomPosition "+bottomPosition);
if (tapPosition != NO_POSITION && layoutManager != null) {
d("MessageList", "Scrolling");
if (bottomPosition > layoutManager.findLastCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(bottomPosition);
}
else if (topPosition < layoutManager.findFirstCompletelyVisibleItemPosition()) {
b.emailList.scrollToPosition(topPosition);
}
else {
b.emailList.scrollToPosition(tapPosition);
}
tapPosition = NO_POSITION;
topPosition = NO_POSITION;
bottomPosition = NO_POSITION;
}
startPostponedEnterTransition();
return true;
}
});*/
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
d("MessagesList", "onSaveInstanceState position "+tapPosition);
outState.putInt(TAP_POSITION, tapPosition);
}
}

View File

@ -0,0 +1,220 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import androidx.core.graphics.ColorUtils;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
import pl.szczodrzynski.edziennik.datamodels.MessageRecipientFull;
import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_DELETED;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_DRAFT;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_RECEIVED;
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_SENT;
public class MessagesUtils {
public static class MessageInfo {
public Bitmap profileImage;
public String profileName;
public MessageInfo(Bitmap profileImage, String profileName) {
this.profileImage = profileImage;
this.profileName = profileName;
}
}
private static String getInitials(String name) {
if (name == null || name.isEmpty())
return "";
name = name.toUpperCase();
String[] nameParts = name.split(" ");
return nameParts.length <= 1 ?
(name.length() == 0 ? "?" : name.charAt(0))+"" :
(nameParts[0].length() == 0 ? "?" : nameParts[0].charAt(0))
+ "" +
(nameParts[1].length() == 0 ? "?" : nameParts[1].charAt(0));
}
private static int getPaintCenter(Paint textPaint) {
return Math.round((textPaint.descent() + textPaint.ascent()) / 2);
}
public static Bitmap getProfileImage(int diameterDp, int textSizeBigDp, int textSizeMediumDp, int textSizeSmallDp, int count, String ... names) {
float diameter = Utils.dpToPx(diameterDp);
float textSizeBig = Utils.dpToPx(textSizeBigDp);
float textSizeMedium = Utils.dpToPx(textSizeMediumDp);
float textSizeSmall = Utils.dpToPx(textSizeSmallDp);
Bitmap bitmap = Bitmap.createBitmap((int) diameter, (int) diameter, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint circlePaint = new Paint();
circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
Paint textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
RectF rectF = new RectF();
rectF.set(0, 0, diameter, diameter);
String name;
int color;
if (count == 1) {
name = names[0];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeBig);
canvas.drawArc(rectF, 0, 360, true, circlePaint);
canvas.drawText(getInitials(name), diameter/2, diameter/2 - getPaintCenter(textPaint), textPaint);
}
else if (count == 2) {
// top
name = names[0];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeMedium);
canvas.drawArc(rectF, 180, 180, true, circlePaint);
canvas.drawText(getInitials(name), diameter/2, diameter/4 - getPaintCenter(textPaint), textPaint);
// bottom
name = names[1];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeMedium);
canvas.drawArc(rectF, 0, 180, true, circlePaint);
canvas.drawText(getInitials(name), diameter/2, diameter/4*3 - getPaintCenter(textPaint), textPaint);
}
else if (count == 3) {
// upper left
name = names[0];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 180, 90, true, circlePaint);
canvas.drawText(getInitials(name), diameter/4, diameter/4 - getPaintCenter(textPaint) + diameter/32, textPaint);
// upper right
name = names[1];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 270, 90, true, circlePaint);
canvas.drawText(getInitials(name), diameter/4*3, diameter/4 - getPaintCenter(textPaint) + diameter/32, textPaint);
// bottom
name = names[2];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeMedium);
canvas.drawArc(rectF, 0, 180, true, circlePaint);
canvas.drawText(getInitials(name), diameter/2, diameter/4*3 - getPaintCenter(textPaint), textPaint);
}
else if (count >= 4) {
// upper left
name = names[0];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 180, 90, true, circlePaint);
canvas.drawText(getInitials(name), diameter/4, diameter/4 - getPaintCenter(textPaint) + diameter/32, textPaint);
// upper right
name = names[1];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 270, 90, true, circlePaint);
canvas.drawText(getInitials(name), diameter/4*3, diameter/4 - getPaintCenter(textPaint) + diameter/32, textPaint);
// bottom left
name = names[2];
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 90, 90, true, circlePaint);
canvas.drawText(getInitials(name), diameter/4, diameter/4*3 - getPaintCenter(textPaint) - diameter/32, textPaint);
// bottom right
if (count == 4)
name = names[3];
if (count > 4)
name = "...";
circlePaint.setColor(color = Colors.stringToMaterialColor(name));
textPaint.setColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
textPaint.setTextSize(textSizeSmall);
canvas.drawArc(rectF, 0, 90, true, circlePaint);
canvas.drawText(count > 4 ? "+"+(count-3) : getInitials(name), diameter/4*3, diameter/4*3 - getPaintCenter(textPaint) - diameter/32, textPaint);
}
return bitmap;
}
public static MessageInfo getMessageInfo(App app, MessageFull message, int diameterDp, int textSizeBigDp, int textSizeMediumDp, int textSizeSmallDp) {
Bitmap profileImage = null;
String profileName = null;
if (message.type == TYPE_RECEIVED || message.type == TYPE_DELETED) {
profileName = message.senderFullName;
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, message.senderFullName);
}
else if (message.type == TYPE_SENT || message.type == TYPE_DRAFT && message.recipients != null) {
int count = message.recipients == null ? 0 : message.recipients.size();
if (count == 0) {
profileName = app.getString(R.string.messages_draft_title);
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, "?");
}
else if (count == 1) {
MessageRecipientFull recipient = message.recipients.get(0);
profileName = recipient.fullName;
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, recipient.fullName);
}
else if (count == 2) {
MessageRecipientFull recipient1 = message.recipients.get(0);
MessageRecipientFull recipient2 = message.recipients.get(1);
profileName = recipient1.fullName+", "+recipient2.fullName;
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 2, recipient1.fullName, recipient2.fullName);
}
else if (count == 3) {
MessageRecipientFull recipient1 = message.recipients.get(0);
MessageRecipientFull recipient2 = message.recipients.get(1);
MessageRecipientFull recipient3 = message.recipients.get(2);
profileName = recipient1.fullName+", "+recipient2.fullName+", "+recipient3.fullName;
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 3, recipient1.fullName, recipient2.fullName, recipient3.fullName);
}
else if (count == 4) {
MessageRecipientFull recipient1 = message.recipients.get(0);
MessageRecipientFull recipient2 = message.recipients.get(1);
MessageRecipientFull recipient3 = message.recipients.get(2);
MessageRecipientFull recipient4 = message.recipients.get(3);
profileName = recipient1.fullName+", "+recipient2.fullName+", "+recipient3.fullName+", "+recipient4.fullName;
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 4, recipient1.fullName, recipient2.fullName, recipient3.fullName, recipient4.fullName);
}
else {
MessageRecipientFull recipient1 = message.recipients.get(0);
MessageRecipientFull recipient2 = message.recipients.get(1);
MessageRecipientFull recipient3 = message.recipients.get(2);
StringBuilder senderText = new StringBuilder();
boolean first = true;
for (MessageRecipientFull recipient: message.recipients) {
if (!first) {
senderText.append(", ");
}
first = false;
senderText.append(recipient.fullName);
}
profileName = senderText.toString();
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, count, recipient1.fullName, recipient2.fullName, recipient3.fullName);
}
}
return new MessageInfo(profileImage, profileName);
}
}

View File

@ -0,0 +1,473 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.core.content.FileProvider;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.MimeTypeMap;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.StackingBehavior;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterMessagesWebBinding;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import static android.app.Activity.RESULT_OK;
import static android.content.Context.DOWNLOAD_SERVICE;
import static pl.szczodrzynski.edziennik.utils.Utils.readableFileSize;
public class RegisterMessagesWebFragment extends Fragment {
private static final String TAG = "RegisterMessagesWeb";
private App app = null;
private Activity activity = null;
private FragmentRegisterMessagesWebBinding b = null;
private WebView webView;
private ProgressBar progressBar;
private TextView error;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activity = 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_messages_web, container, false);
return b.getRoot();
}
private boolean isStoragePermissionGranted(Activity a) {
if (Build.VERSION.SDK_INT >= 23) {
if (a.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
return true;
} else {
Log.v(TAG,"Permission is revoked");
ActivityCompat.requestPermissions(a, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG,"Permission is granted");
return true;
}
}
private String getFileNameFromDisposition(String header)
{
return header.substring(header.indexOf("\"") + 1, header.lastIndexOf("\""));
}
private File downloadingFile;
private void enqueueFile(String url, String filename, File downloadingFile, String cookieString) {
this.downloadingFile = downloadingFile;
long downloadReference;
DownloadManager downloadManager;
downloadManager = (DownloadManager)app.getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setTitle(filename);
request.setDescription(getString(R.string.downloading));
request.addRequestHeader("Cookie", cookieString);
try {
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
}
catch (IllegalStateException e)
{
e.printStackTrace();
Toast.makeText(app, "Failed to get external storage files directory", Toast.LENGTH_SHORT).show();
}
downloadReference = downloadManager.enqueue(request);
}
private static String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
private void openFile(File url) {
Toast.makeText(app, getString(R.string.opening_file, url.getName()), Toast.LENGTH_SHORT).show();
try {
Uri uri = FileProvider.getUriForFile(app, app.getApplicationContext().getPackageName() + ".provider", url);
//Uri uri = Uri.fromFile(url);
Intent intent = new Intent(Intent.ACTION_VIEW);
if (url.toString().contains(".doc") || url.toString().contains(".docx")) {
// Word document
intent.setDataAndType(uri, "application/msword");
} else if (url.toString().contains(".pdf")) {
// PDF file
intent.setDataAndType(uri, "application/pdf");
} else if (url.toString().contains(".ppt") || url.toString().contains(".pptx")) {
// Powerpoint file
intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
} else if (url.toString().contains(".xls") || url.toString().contains(".xlsx")) {
// Excel file
intent.setDataAndType(uri, "application/vnd.ms-excel");
} else if (url.toString().contains(".zip") || url.toString().contains(".rar")) {
// WAV audio file
intent.setDataAndType(uri, "application/x-wav");
} else if (url.toString().contains(".rtf")) {
// RTF file
intent.setDataAndType(uri, "application/rtf");
} else if (url.toString().contains(".wav") || url.toString().contains(".mp3")) {
// WAV audio file
intent.setDataAndType(uri, "audio/x-wav");
} else if (url.toString().contains(".gif")) {
// GIF file
intent.setDataAndType(uri, "image/gif");
} else if (url.toString().contains(".jpg") || url.toString().contains(".jpeg") || url.toString().contains(".png")) {
// JPG file
intent.setDataAndType(uri, "image/jpeg");
} else if (url.toString().contains(".txt")) {
// Text file
intent.setDataAndType(uri, "text/plain");
} else if (url.toString().contains(".3gp") || url.toString().contains(".mpg") ||
url.toString().contains(".mpeg") || url.toString().contains(".mpe") || url.toString().contains(".mp4") || url.toString().contains(".avi")) {
// Video files
intent.setDataAndType(uri, "video/*");
} else {
intent.setDataAndType(uri, "*/*");
}
intent.setDataAndType(uri, getMimeType(uri.toString()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
app.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(app, R.string.opening_file_no_app, Toast.LENGTH_SHORT).show();
}
}
private void downloadFile(Activity a, String url, String filename, long contentLength, String cookieString) {
if (!isStoragePermissionGranted(a))
return;
new MaterialDialog.Builder(a)
.title(R.string.downloading_file)
.content(getString((R.string.download_file_question), filename, readableFileSize(contentLength)))
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive((dialog, which) -> {
File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File existingFile = new File(downloadsDir, filename);
if (existingFile.exists()) {
new MaterialDialog.Builder(a)
.title(R.string.downloading_file)
.content(getString(R.string.downloading_file_exists_choose, filename))
.stackingBehavior(StackingBehavior.ADAPTIVE)
.positiveText(R.string.downloading_file_exists_overwrite)
.negativeText(R.string.downloading_file_exists_open)
.neutralText(R.string.downloading_file_exists_create_new)
.onPositive(((dialog1, which1) -> {
if (!existingFile.delete())
{
new MaterialDialog.Builder(a)
.title(R.string.downloading_file)
.content(R.string.downloading_file_cannot_remove)
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.onPositive(((dialog2, which2) -> enqueueFile(url, filename, existingFile, cookieString)))
.show();
return;
}
enqueueFile(url, filename, existingFile, cookieString);
}))
.onNegative(((dialog1, which1) -> openFile(existingFile)))
.onNeutral((dialog1, which1) -> enqueueFile(url, filename, existingFile, cookieString))
.show();
return;
}
enqueueFile(url, filename, existingFile, cookieString);
})
.show();
}
private String photoPath;
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> fileCallback;
private final static int REQUEST_FILE_CHOOSER = 1;
private boolean justLoaded = false;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(Build.VERSION.SDK_INT >= 21){
Uri[] results = null;
//Check if response is positive
if(resultCode == RESULT_OK){
if(requestCode == REQUEST_FILE_CHOOSER){
if(null == fileCallback){
return;
}
if(data == null){
//Capture Photo if no image available
if (photoPath != null) {
results = new Uri[]{Uri.parse(photoPath)};
}
}else{
String dataString = data.getDataString();
if(dataString != null){
results = new Uri[]{Uri.parse(dataString)};
}
}
}
}
fileCallback.onReceiveValue(results);
fileCallback = null;
} else {
if (requestCode == REQUEST_FILE_CHOOSER) {
if(null == mUM) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
mUM.onReceiveValue(result);
mUM = null;
}
}
}
private File createImageFile() throws IOException{
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "img_"+timeStamp+"_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return File.createTempFile(imageFileName,".jpg",storageDir);
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
webView = b.messagesWebView;
progressBar = b.messagesWebProgressBar;
error = b.messagesWebError;
justLoaded = true;
new Handler().postDelayed(() -> activity.runOnUiThread(() -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (downloadingFile != null) {
openFile(downloadingFile);
downloadingFile = null;
}
}
};
activity.registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
app.apiEdziennik.initMessagesWebView(webView, app, /*app.profile.messagesWebFullVersion*/false, false);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
String filename = getFileNameFromDisposition(contentDisposition);
try {
URL urlObj = new URL(url);
Log.d(TAG, "Host "+urlObj.getProtocol()+"://"+urlObj.getHost());
Log.d(TAG, "Cookies "+CookieManager.getInstance().getCookie(urlObj.getProtocol()+"://"+urlObj.getHost()));
downloadFile(getActivity(), url, filename, contentLength, CookieManager.getInstance().getCookie(urlObj.getProtocol()+"://"+urlObj.getHost()));
} catch (MalformedURLException e) {
e.printStackTrace();
}
Anim.fadeOut(progressBar, 400, null);
});
webView.setWebViewClient(new WebViewClient() {
boolean loadingFinished = true;
boolean redirect = false;
@Override
public boolean shouldOverrideUrlLoading(WebView view, String request) {
if (!loadingFinished) {
redirect = true;
}
loadingFinished = false;
webView.loadUrl(request);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
RegisterMessagesWebFragment.this.error.setVisibility(View.GONE);
loadingFinished = false;
//SHOW LOADING IF IT ISNT ALREADY VISIBLE
if (progressBar.getVisibility() != View.VISIBLE)
Anim.fadeIn(progressBar, 400, null);
}
@Override
public void onPageFinished(WebView view, String url) {
if (!redirect) {
loadingFinished = true;
}
if (loadingFinished && !redirect) {
//HIDE LOADING IT HAS FINISHED
//String cookies = CookieManager.getInstance().getCookie(url);
//Log.d(TAG, "All the cookies in a string:" + cookies);
Anim.fadeOut(progressBar, 400, null);
/*if (app.profile.messagesWebFullVersion && justLoaded && app.profile.loginType == Register.LOGIN_TYPE_MOBIDZIENNIK) {
if (!webView.getUrl().contains("wiadomosci")) {
// redirect to messages view
webView.loadUrl("https://" + app.getLoginData("serverName", "") + ".mobidziennik.pl/mobile/wiadomosci");
}
justLoaded = false;
}*/
} else {
redirect = false;
}
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
RegisterMessagesWebFragment.this.error.setVisibility(View.VISIBLE);
RegisterMessagesWebFragment.this.error.setText(getString(R.string.error_occured_format, error.toString()));
super.onReceivedError(view, request, error);
}
});
if(Build.VERSION.SDK_INT >= 21) {
webView.getSettings().setMixedContentMode(0);
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if(Build.VERSION.SDK_INT >= 19) {
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else {
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webView.setWebChromeClient(new WebChromeClient(){
//For Android 5.0+
public boolean onShowFileChooser(
WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams){
if(fileCallback != null){
fileCallback.onReceiveValue(null);
}
fileCallback = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(takePictureIntent.resolveActivity(app.getPackageManager()) != null){
File photoFile = null;
try{
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", photoPath);
}catch(IOException ex){
Log.e(TAG, "Image file creation failed", ex);
}
if(photoFile != null){
photoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
}else{
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
if(takePictureIntent != null){
intentArray = new Intent[]{takePictureIntent};
}else{
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, R.string.choose_file);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, REQUEST_FILE_CHOOSER);
return true;
}
});
}), 200);
}
/*public void loadVersion(boolean fullVersion) {
if (app.profile.messagesWebFullVersion != fullVersion) {
app.profile.messagesWebFullVersion = fullVersion;
app.profile.savePending = true;
justLoaded = true;
app.apiEdziennik.initMessagesWebView(webView, app, fullVersion, true);
}
}*/
public void performReload() {
webView.reload();
}
public boolean processBackKey()
{
if (webView.canGoBack())
{
webView.goBack();
return true;
}
return false;
}
}

View File

@ -0,0 +1,75 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.transition.TransitionValues;
import androidx.transition.Visibility;
public class SlideExplode extends Visibility {
private static final String KEY_SCREEN_BOUNDS = "screenBounds";
private int[] mTempLoc = new int[2];
private void captureValues(TransitionValues transitionValues) {
View view = transitionValues.view;
view.getLocationOnScreen(mTempLoc);
int left = mTempLoc[0];
int top = mTempLoc[1];
int right = left + view.getWidth();
int bottom = top + view.getHeight();
transitionValues.values.put(KEY_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
}
@Override
public void captureStartValues(@NonNull TransitionValues transitionValues) {
super.captureStartValues(transitionValues);
captureValues(transitionValues);
}
@Override
public void captureEndValues(@NonNull TransitionValues transitionValues) {
super.captureEndValues(transitionValues);
captureValues(transitionValues);
}
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (endValues == null)
return null;
Rect bounds = (Rect) endValues.values.get(KEY_SCREEN_BOUNDS);
float endY = view.getTranslationY();
float startY = endY + calculateDistance(sceneRoot, bounds);
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null)
return null;
Rect bounds = (Rect) startValues.values.get(KEY_SCREEN_BOUNDS);
float startY = view.getTranslationY();
float endY = startY + calculateDistance(sceneRoot, bounds);
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
}
private int calculateDistance(View sceneRoot, Rect viewBounds) {
sceneRoot.getLocationOnScreen(mTempLoc);
int sceneRootY = mTempLoc[1];
if (getEpicenter() == null) {
return -sceneRoot.getHeight();
}
else if (viewBounds.top <= getEpicenter().top) {
return sceneRootY - getEpicenter().top;
}
else {
return sceneRootY + sceneRoot.getHeight() - getEpicenter().bottom;
}
}
}

View File

@ -0,0 +1,18 @@
package pl.szczodrzynski.edziennik.ui.modules.messages;
import android.animation.TimeInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.transition.TransitionSet;
public class Transitions extends TransitionSet {
@NonNull
@Override
public TransitionSet setInterpolator(@Nullable TimeInterpolator interpolator) {
for (int i = 0; i < getTransitionCount(); i++) {
getTransitionAt(i).setInterpolator(interpolator);
}
return this;
}
}

View File

@ -0,0 +1,91 @@
package pl.szczodrzynski.edziennik.ui.modules.notices
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.AsyncTask
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.mikepenz.iconics.IconicsDrawable
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorRes
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.datamodels.Notice
import pl.szczodrzynski.edziennik.datamodels.NoticeFull
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.utils.Utils.bs
class NoticesAdapter//getting the context and product list with constructor
(private val context: Context, var noticeList: List<NoticeFull>) : RecyclerView.Adapter<NoticesAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
//inflating and returning our view holder
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.row_notices_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val app = context.applicationContext as App
val notice = noticeList[position]
if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK) {
holder.noticesItemReason.text = bs(null, notice.category, "\n") + notice.text
holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherFullName, if (notice.points > 0) "+" + notice.points else notice.points)
} else {
holder.noticesItemReason.text = notice.text
holder.noticesItemTeacherName.text = notice.teacherFullName
}
holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString
if (notice.type == Notice.TYPE_POSITIVE) {
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle)
.colorRes(R.color.md_green_600)
.sizeDp(36))
} else if (notice.type == Notice.TYPE_NEGATIVE) {
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram)
.colorRes(R.color.md_red_600)
.sizeDp(36))
} else {
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_message_outline)
.colorRes(R.color.md_blue_500)
.sizeDp(36))
}
if (!notice.seen) {
holder.noticesItemReason.background = context.resources.getDrawable(R.drawable.bg_rounded_8dp)
when {
notice.type == Notice.TYPE_POSITIVE -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
notice.type == Notice.TYPE_NEGATIVE -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
else -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
}
notice.seen = true
AsyncTask.execute { app.db.metadataDao().setSeen(App.profileId, notice, true) }
} else {
holder.noticesItemReason.background = null
}
}
override fun getItemCount(): Int {
return noticeList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var noticesItemCard: CardView = itemView.findViewById(R.id.noticesItemCard)
var noticesItemType: ImageView = itemView.findViewById(R.id.noticesItemType)
var noticesItemReason: TextView = itemView.findViewById(R.id.noticesItemReason)
var noticesItemTeacherName: TextView = itemView.findViewById(R.id.noticesItemTeacherName)
var noticesItemAddedDate: TextView = itemView.findViewById(R.id.noticesItemAddedDate)
}
}

View File

@ -0,0 +1,174 @@
package pl.szczodrzynski.edziennik.ui.modules.notices;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.PopupMenu;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterNoticesBinding;
import pl.szczodrzynski.edziennik.datamodels.Notice;
import pl.szczodrzynski.edziennik.datamodels.NoticeFull;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_NOTICE;
public class RegisterNoticesFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentRegisterNoticesBinding 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 List<NoticeFull> noticeList = 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_notices, 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_NOTICE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_NOTICES, b.refreshLayout);
});*/
b.noticesSummaryTitle.setOnClickListener((v -> {
PopupMenu popupMenu = new PopupMenu(activity, b.noticesSummaryTitle, 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();
}));
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
b.noticesView.setHasFixedSize(true);
b.noticesView.setLayoutManager(linearLayoutManager);
app.db.noticeDao().getAll(App.profileId).observe(this, notices -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
if (notices == null) {
b.noticesView.setVisibility(View.GONE);
b.noticesNoData.setVisibility(View.VISIBLE);
return;
}
noticeList = notices;
updateList();
});
}
private void updateList() {
int praisesCount = 0;
int warningsCount = 0;
int otherCount = 0;
List<NoticeFull> filteredList = new ArrayList<>();
for (NoticeFull notice: noticeList) {
if (displayMode != MODE_YEAR && notice.semester != displayMode)
continue;
filteredList.add(notice);
switch (notice.type) {
case Notice.TYPE_POSITIVE:
praisesCount++;
break;
case Notice.TYPE_NEGATIVE:
warningsCount++;
break;
case Notice.TYPE_NEUTRAL:
otherCount++;
break;
}
}
if (filteredList.size() > 0) {
NoticesAdapter adapter;
b.noticesView.setVisibility(View.VISIBLE);
b.noticesNoData.setVisibility(View.GONE);
if ((adapter = (NoticesAdapter) b.noticesView.getAdapter()) != null) {
adapter.setNoticeList(filteredList);
adapter.notifyDataSetChanged();
}
else {
adapter = new NoticesAdapter(getContext(), filteredList);
b.noticesView.setAdapter(adapter);
}
}
else {
b.noticesView.setVisibility(View.GONE);
b.noticesNoData.setVisibility(View.VISIBLE);
}
if (displayMode == MODE_YEAR) {
b.noticesSummaryTitle.setText(getString(R.string.notices_summary_title_year));
}
else {
b.noticesSummaryTitle.setText(getString(R.string.notices_summary_title_semester_format, displayMode));
}
b.noticesPraisesCount.setText(String.format(Locale.getDefault(), "%d", praisesCount));
b.noticesWarningsCount.setText(String.format(Locale.getDefault(), "%d", warningsCount));
b.noticesOtherCount.setText(String.format(Locale.getDefault(), "%d", otherCount));
if (warningsCount >= 3) {
b.noticesWarningsCount.setTextColor(Color.RED);
}
else {
b.noticesWarningsCount.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
}
}
}

View File

@ -0,0 +1,104 @@
package pl.szczodrzynski.edziennik.ui.modules.notifications;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Notification;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.ViewHolder> {
private static final String TAG = "NotificationsAdapter";
private Context context;
private List<Notification> notificationList;
//getting the context and product list with constructor
public NotificationsAdapter(Context mCtx, List<Notification> notificationList) {
this.context = mCtx;
this.notificationList = notificationList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_notifications_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
Notification notification = notificationList.get(position);
holder.notificationsItemDate.setText(Date.fromMillis(notification.addedDate).getFormattedString());
holder.notificationsItemText.setText(notification.text);
holder.notificationsItemTitle.setText(notification.title);
holder.notificationsItemType.setText(Notification.stringType(context, notification.type));
holder.notificationsItemCard.setOnClickListener((v -> {
Intent intent = new Intent("android.intent.action.MAIN");
notification.fillIntent(intent);
d(TAG, "notification with item "+notification.redirectFragmentId+" extras "+(intent.getExtras() == null ? "null" : intent.getExtras().toString()));
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
if (notification.profileId != -1 && notification.profileId != app.profile.getId() && context instanceof Activity) {
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show();
}
app.sendBroadcast(intent);
}));
if (!notification.seen) {
holder.notificationsItemText.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
holder.notificationsItemText.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
}
else {
holder.notificationsItemText.setBackground(null);
}
}
@Override
public int getItemCount() {
return notificationList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView notificationsItemCard;
TextView notificationsItemDate;
TextView notificationsItemText;
TextView notificationsItemTitle;
TextView notificationsItemType;
ViewHolder(View itemView) {
super(itemView);
notificationsItemCard = itemView.findViewById(R.id.notificationsItemCard);
notificationsItemDate = itemView.findViewById(R.id.notificationsItemDate);
notificationsItemText = itemView.findViewById(R.id.notificationsItemText);
notificationsItemTitle = itemView.findViewById(R.id.notificationsItemTitle);
notificationsItemType = itemView.findViewById(R.id.notificationsItemType);
}
}
}

View File

@ -0,0 +1,64 @@
package pl.szczodrzynski.edziennik.ui.modules.notifications;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterNotificationsBinding;
import pl.szczodrzynski.edziennik.utils.Themes;
public class RegisterNotificationsFragment extends Fragment {
private App app = null;
private Activity activity = null;
private FragmentRegisterNotificationsBinding b = null;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
activity = 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_notifications, container, false);
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;
RecyclerView recyclerView = b.notificationsView;
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
if (app.appConfig.notifications.size() > 0) {
NotificationsAdapter adapter = new NotificationsAdapter(getContext(), app.appConfig.notifications);
recyclerView.setAdapter(adapter);
recyclerView.setVisibility(View.VISIBLE);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
//linearLayoutManager.setReverseLayout(true);
//linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
b.notificationsNoData.setVisibility(View.GONE);
}
else {
recyclerView.setVisibility(View.GONE);
b.notificationsNoData.setVisibility(View.VISIBLE);
}
}
}

View File

@ -0,0 +1,43 @@
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentProfileManagerBinding
import pl.szczodrzynski.edziennik.utils.Themes
class ProfileManagerFragment : Fragment() {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var b: FragmentProfileManagerBinding
/*
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
*/
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
if (context == null)
return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = FragmentProfileManagerBinding.inflate(inflater)
b.refreshLayout.setParent(activity.swipeRefreshLayout)
return b.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (app.profile == null || !isAdded)
return
}
}

View File

@ -0,0 +1,193 @@
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.net.Uri
import android.os.Bundle
import com.danielstone.materialaboutlibrary.ConvenienceBuilder
import com.danielstone.materialaboutlibrary.MaterialAboutActivity
import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.danielstone.materialaboutlibrary.model.MaterialAboutList
import com.danielstone.materialaboutlibrary.util.OpenSourceLicense
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.utils.Themes
class SettingsLicenseActivity : MaterialAboutActivity() {
var foregroundColor: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
val app = application as App
setTheme(Themes.appTheme)
foregroundColor = Themes.getPrimaryTextColor(this)
super.onCreate(savedInstanceState)
}
private fun createLicenseCard(
context: Context,
libraryTitle: CharSequence,
copyrightYear: CharSequence,
copyrightName: CharSequence,
license: OpenSourceLicense,
libraryUrl: String): MaterialAboutCard {
val licenseItem = MaterialAboutActionItem.Builder()
.icon(IconicsDrawable(this)
.icon(CommunityMaterial.Icon.cmd_book)
.colorInt(foregroundColor)
.sizeDp(18))
.setIconGravity(MaterialAboutActionItem.GRAVITY_TOP)
.text(libraryTitle)
.subText(String.format(getString(license.resourceId), copyrightYear, copyrightName))
.setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(context, Uri.parse(libraryUrl)))
.build()
return MaterialAboutCard.Builder().addItem(licenseItem).build()
}
override fun getMaterialAboutList(context: Context): MaterialAboutList {
return MaterialAboutList(
createLicenseCard(this,
"OkHttp",
"",
"square",
OpenSourceLicense.APACHE_2,
"https://github.com/square/okhttp/"),
createLicenseCard(this,
"MHttp",
"2018",
"Mot.",
OpenSourceLicense.APACHE_2,
"https://github.com/motcwang/MHttp/"),
createLicenseCard(this,
"AgendaCalendarView",
"2015",
"Thibault Guégan",
OpenSourceLicense.APACHE_2,
"https://github.com/Tibolte/AgendaCalendarView/"),
createLicenseCard(this,
"Material Calendar View",
"2017",
"Applandeo sp. z o.o.",
OpenSourceLicense.APACHE_2,
"https://github.com/Applandeo/Material-Calendar-View/"),
createLicenseCard(this,
"Android-Job",
"2007-2017",
"Evernote Corporation",
OpenSourceLicense.APACHE_2,
"https://github.com/evernote/android-job/"),
createLicenseCard(this,
"Custom Activity On Crash",
"",
"Eduard Ereza MartĂ­nez (Ereza)",
OpenSourceLicense.APACHE_2,
"https://github.com/Ereza/CustomActivityOnCrash/"),
createLicenseCard(this,
"Android-Iconics",
"2018",
"Mike Penz",
OpenSourceLicense.APACHE_2,
"https://github.com/mikepenz/Android-Iconics/"),
createLicenseCard(this,
"MaterialDrawer",
"2016",
"Mike Penz",
OpenSourceLicense.APACHE_2,
"https://github.com/mikepenz/MaterialDrawer/"),
createLicenseCard(this,
"Material Dialogs",
"2014-2016",
"Aidan Michael Follestad",
OpenSourceLicense.MIT,
"https://github.com/afollestad/material-dialogs/"),
createLicenseCard(this,
"MaterialDateTimePicker",
"2014",
"Wouter Dullaert",
OpenSourceLicense.APACHE_2,
"https://github.com/wdullaer/MaterialDateTimePicker/"),
createLicenseCard(this,
"ColorPicker",
"2016",
"Jared Rummler, 2015 Daniel Nilsson",
OpenSourceLicense.APACHE_2,
"https://github.com/jaredrummler/ColorPicker/"),
createLicenseCard(this,
"material-about-library",
"2016-2018",
"Daniel Stone",
OpenSourceLicense.APACHE_2,
"https://github.com/daniel-stoneuk/material-about-library/"),
createLicenseCard(this,
"material-intro",
"2017",
"Jan Heinrich Reimer",
OpenSourceLicense.MIT,
"https://github.com/heinrichreimer/material-intro/"),
createLicenseCard(this,
"JsonViewer",
"2017",
"smuyyh",
OpenSourceLicense.APACHE_2,
"https://github.com/smuyyh/JsonViewer/"),
createLicenseCard(this,
"ShortcutBadger",
"2014",
"Leo Lin",
OpenSourceLicense.APACHE_2,
"https://github.com/leolin310148/ShortcutBadger/"),
createLicenseCard(this,
"Android Image Cropper",
"2016",
"Arthur Teplitzki, 2013 Edmodo, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/ArthurHub/Android-Image-Cropper/"),
createLicenseCard(this,
"Material Tap Target Prompt",
"2016-2018",
"Samuel Wall",
OpenSourceLicense.APACHE_2,
"https://github.com/sjwall/MaterialTapTargetPrompt/"),
createLicenseCard(this,
"Android Swipe Layout",
"2014",
"代码家 (daimajia)",
OpenSourceLicense.MIT,
"https://github.com/daimajia/AndroidSwipeLayout/"),
createLicenseCard(this,
"barcodescanner (ZXing)",
"2014",
"Dushyanth Maguluru",
OpenSourceLicense.APACHE_2,
"https://github.com/dm77/barcodescanner/"),
createLicenseCard(this,
"CircularProgressIndicator",
"2018",
"Anton Kozyriatskyi",
OpenSourceLicense.APACHE_2,
"https://github.com/antonKozyriatskyi/CircularProgressIndicator/")
/*createLicenseCard(this,
"NoNonsense-FilePicker",
"",
"Jonas Kalderstam (spacecowboy)",
OpenSourceLicense.GNU_GPL_3,
"https://github.com/spacecowboy/NoNonsense-FilePicker/")*/
)
}
override fun getActivityTitle(): CharSequence? {
return getString(R.string.settings_about_licenses_text)
}
}

View File

@ -0,0 +1,89 @@
package pl.szczodrzynski.edziennik.ui.modules.timetable;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterTimetableDayBinding;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.Themes;
public class RegisterTimetableDayFragment extends Fragment {
private App app = null;
private MainActivity activity = null;
private FragmentRegisterTimetableDayBinding b = null;
private RecyclerView recyclerView;
@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_timetable_day, 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;
/*b.getRoot().setOnTouchListener((v, event) -> {
d("TimetableDay", "event "+event);
event.setSource(0x10000000); // set a unique source
activity.swipeRefreshLayout.onTouchEvent(event);
return true;
});*/
//b.refreshLayout.setNestedScrollingEnabled(true);
/*b.refreshLayout.setOnRefreshListener(() -> {
activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_TIMETABLE, b.refreshLayout);
});*/
assert getArguments() != null;
Date date = new Date().parseFromYmd(Long.toString(getArguments().getLong("date", 20181009)));
recyclerView = b.timetableView;
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
app.db.lessonDao().getAllByDate(App.profileId, date, Time.getNow()).observe(this, lessons -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
if (lessons != null && lessons.size() > 0) {
app.db.eventDao().getAllByDate(App.profileId, date).observe(this, events -> {
TimetableAdapter adapter = new TimetableAdapter(getContext(), date, lessons, events == null ? new ArrayList<>() : events);
recyclerView.setAdapter(adapter);
recyclerView.setVisibility(View.VISIBLE);
b.timetableNoData.setVisibility(View.GONE);
});
}
else {
recyclerView.setVisibility(View.GONE);
b.timetableNoData.setVisibility(View.VISIBLE);
}
});
}
}

View File

@ -0,0 +1,628 @@
package pl.szczodrzynski.edziennik.ui.modules.timetable;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.google.android.material.tabs.TabLayout;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import androidx.cardview.widget.CardView;
import android.os.Environment;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.databinding.FragmentRegisterTimetableBinding;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.models.Week;
import pl.szczodrzynski.edziennik.utils.SpannableHtmlTagHandler;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CANCELLED;
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CHANGE;
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_LESSON_CHANGE;
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class RegisterTimetableFragment extends Fragment {
private static final String TAG = "RegisterTimetable";
private App app = null;
private MainActivity activity = null;
private FragmentRegisterTimetableBinding b = null;
private ViewPager viewPager;
private static int pageSelection = -1;
private static Date displayingDate;
@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_timetable, container, false);
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_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
new MaterialDialog.Builder(activity)
.title(R.string.main_menu_add)
.items(R.array.main_menu_add_options)
.itemsCallback((dialog, itemView, position, text) -> {
switch (position) {
case 0:
new EventManualDialog(activity).show(app, null, displayingDate, null, EventManualDialog.DIALOG_EVENT);
break;
case 1:
new EventManualDialog(activity).show(app, null, displayingDate, null, EventManualDialog.DIALOG_HOMEWORK);
break;
}
})
.show();
}),
new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_generate_block_timetable)
.withDescription(R.string.menu_generate_block_timetable_desc)
.withIcon(CommunityMaterial.Icon2.cmd_table_large)
.withOnClickListener(v3 -> {
activity.getBottomSheet().close();
generateBlockTimetable();
}),
new BottomSheetSeparatorItem(true),
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_LESSON_CHANGE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
})
);
activity.gainAttention();
// Setting ViewPager for each Tabs
viewPager = b.viewpager;
Adapter adapter = new Adapter(getChildFragmentManager());
Date today = Date.getToday();
Date date = Date.getToday();
int weekBeginning = app.appConfig.timetableDisplayDaysBackward - date.getWeekDay();
int weekEnd = weekBeginning + 6;
date.stepForward(0, 0, 0 - (app.appConfig.timetableDisplayDaysBackward));
for (int i = 0; i < app.appConfig.timetableDisplayDaysForward + app.appConfig.timetableDisplayDaysBackward + 1; i++) {
Bundle args = new Bundle();
args.putLong("date", date.getValue());
RegisterTimetableDayFragment registerTimetableDayFragment = new RegisterTimetableDayFragment();
registerTimetableDayFragment.setArguments(args);
StringBuilder pageTitle = new StringBuilder(Week.getFullDayName(date.getWeekDay()));
if (i > weekEnd || i < weekBeginning) {
pageTitle.append(", ").append(date.getStringDm());
}
adapter.addFragment(registerTimetableDayFragment, pageTitle.toString());
date.stepForward(0, 0, 1);
}
viewPager.setAdapter(adapter);
if (getArguments() != null && getArguments().getLong("timetableDate", 0) != 0) {
Date gotDate = new Date().parseFromYmd(Long.toString(getArguments().getLong("timetableDate", 0))); // OVERRIDE HERE
// DAMNIT
// THE TIMETABLE WAS DOING LOTS OF WEIRD THINGS (incorrect default days, sometimes scrolling to the beginning)
// BECAUSE THESE TWO LINES WERE SWAPPED.
//pageSelection += Date.diffDays(gotDate, displayingDate);
Log.d(TAG, "Got date "+getArguments().getLong("timetableDate", 0));
pageSelection = app.appConfig.timetableDisplayDaysBackward + Date.diffDays(gotDate, today);
displayingDate = gotDate;
}
else if (pageSelection == -1) {
AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
return;
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today);
displayingDate = HomeFragment.findDateWithLessons(App.profileId, lessons);
pageSelection = app.appConfig.timetableDisplayDaysBackward + Date.diffDays(displayingDate, today); // DEFAULT HERE
activity.runOnUiThread(() -> {
viewPager.setCurrentItem(pageSelection, false);
});
});
}
viewPager.setCurrentItem(pageSelection, false);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
@Override public void onPageScrollStateChanged(int state) { }
@Override public void onPageSelected(int position) {
pageSelection = position;
Fragment fragment = adapter.getItem(position);
assert fragment.getArguments() != null;
displayingDate = new Date().parseFromYmd(Long.toString(fragment.getArguments().getLong("date", 20181009)));
/*
Fragment fragment = adapter.getItem(position);
int scrolledDate = fragment.getArguments().getInt("date", 0);
//Toast.makeText(app, "Date: "+scrolledDate, Toast.LENGTH_SHORT).show();
Collection<Integer> removeDates = new ArrayList<>();
for (Integer lessonChangeDate: unreadLessonChangesDates) {
if (lessonChangeDate.equals(scrolledDate)) {
for (RegisterLessonChange lessonChange: app.profile.timetable.lessonChanges) {
if (lessonChange.lessonDate.getValue() == lessonChangeDate) {
lessonChange.notified = true;
}
}
removeDates.add(lessonChangeDate);
}
}
unreadLessonChangesDates.removeAll(removeDates);
if (app.profile.unreadLessonChanges != unreadLessonChangesDates.size()) {
app.profile.unreadLessonChanges = unreadLessonChangesDates.size();
app.profile.savePending = true;
Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure");
getContext().sendBroadcast(i);
}
}*/
}
});
// Set Tabs inside Toolbar
TabLayout tabs = view.findViewById(R.id.result_tabs);
tabs.setupWithViewPager(viewPager);
/*if (!app.appConfig.tapTargetSetAsRead) {
new MaterialTapTargetPrompt.Builder(activity)
.setTarget(activity.findViewById(R.id.action_mark_as_read))
.setPrimaryText(R.string.tap_target_set_as_read_title)
.setSecondaryText(R.string.tap_target_set_as_read_text)
.setFocalColour(Color.TRANSPARENT)
.setPromptStateChangeListener((prompt, state) -> {
if (state == MaterialTapTargetPrompt.STATE_FOCAL_PRESSED) {
// User has pressed the prompt target
}
})
.show();
app.appConfig.tapTargetSetAsRead = true;
app.appConfig.savePending = true;
}*/
}
public static Bitmap getBitmapFromView(View view) {
//Define a bitmap with the same size as the view
Bitmap returnedBitmap = Bitmap.createBitmap(view.getLayoutParams().width, view.getLayoutParams().height, Bitmap.Config.ARGB_8888);
//Bind a canvas to it
Canvas canvas = new Canvas(returnedBitmap);
//Get the view's background
Drawable bgDrawable = view.getBackground();
if (bgDrawable!=null)
//has background drawable, then draw it on the canvas
bgDrawable.draw(canvas);
else
//does not have background drawable, then draw white background on the canvas
canvas.drawColor(Color.TRANSPARENT);
// draw the view on the canvas
view.draw(canvas);
//return the bitmap
return returnedBitmap;
}
public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, true);
bm.recycle();
return resizedBitmap;
}
public void generateBlockTimetable() {
if (getActivity() == null) {
return;
}
Date weekCurrentStart = Week.getWeekStart();
Date weekCurrentEnd = Week.getWeekEnd();
Date weekNextStart = weekCurrentEnd.clone().stepForward(0, 0, 1);
Date weekNextEnd = weekNextStart.clone().stepForward(0, 0, 6);
new MaterialDialog.Builder(getActivity())
.title(R.string.timetable_generate_range)
.items(
getString(R.string.timetable_generate_no_changes),
getString(R.string.timetable_generate_current_week_format, weekCurrentStart.getFormattedStringShort(), weekCurrentEnd.getFormattedStringShort()),
getString(R.string.timetable_generate_next_week_format, weekNextStart.getFormattedStringShort(), weekNextEnd.getFormattedStringShort()),
getString(R.string.timetable_generate_for_printout))
.positiveText(R.string.ok)
.negativeText(R.string.cancel)
.checkBoxPromptRes(R.string.timetable_generate_include_profile_name, true, null)
.itemsCallbackSingleChoice(0, (dialog, itemView, which, text) -> {
Toast.makeText(app, "Selected "+which, Toast.LENGTH_SHORT).show();
AsyncTask.execute(() -> {
switch (which) {
case 0:
generateBlockTimetableWithLessons(app.db.lessonDao().getAllWeekNow(App.profileId, weekCurrentStart, weekCurrentStart), false, dialog.isPromptCheckBoxChecked(), null, null, false);
break;
case 1:
generateBlockTimetableWithLessons(app.db.lessonDao().getAllWeekNow(App.profileId, weekCurrentStart, weekCurrentStart), true, dialog.isPromptCheckBoxChecked(), weekCurrentStart, weekCurrentEnd, false);
break;
case 2:
generateBlockTimetableWithLessons(app.db.lessonDao().getAllWeekNow(App.profileId, weekNextStart, weekNextStart), true, dialog.isPromptCheckBoxChecked(), weekNextStart, weekNextEnd, false);
break;
case 3:
generateBlockTimetableWithLessons(app.db.lessonDao().getAllWeekNow(App.profileId, weekCurrentStart, weekCurrentStart), false, dialog.isPromptCheckBoxChecked(), null, null, true);
break;
}
});
if (which == 0) {
}
return false;
})
.show();
}
private MaterialDialog progressDialog;
private void generateBlockTimetableWithLessons(List<LessonFull> lessonList, boolean markChanges, boolean showProfileName, Date weekStart, Date weekEnd, boolean noColors) {
d(TAG, Arrays.toString(lessonList.toArray()));
activity.runOnUiThread(() -> {
progressDialog = new MaterialDialog.Builder(activity)
.title(R.string.timetable_generate_progress_title)
.content(R.string.timetable_generate_progress_text)
.progress(true, 0)
.show();
});
// block size: 190x90, so one minute is 2px
// spacing: 15x10
// left size: 45px
// header size: 45px
// overall width: 45 + n*(190+15)
// overall height: 45 + n*(90+10)
// footer size 30px
int WIDTH_CONSTANT = 70;
int WIDTH_WEEKDAY = 285;
int WIDTH_SPACING = 15;
int HEIGHT_PROFILE_NAME = showProfileName ? 100 : 0;
int HEIGHT_CONSTANT = 60;
int HEIGHT_MINUTE = 3;
int HEIGHT_FOOTER = 40;
List<List<LessonFull>> weekdays = new ArrayList<>();
for(int i = 0; i < 7; i++) {
weekdays.add(new ArrayList<>());
}
int maxWeekDay = 5;
Time minTime = null;
Time maxTime = null;
TreeMap<Integer, Integer> lessonRanges = new TreeMap<>();
for (LessonFull lesson: lessonList) {
if (lesson.weekDay > maxWeekDay)
maxWeekDay = lesson.weekDay;
List<LessonFull> weekdayLessons = weekdays.get(lesson.weekDay);
weekdayLessons.add(lesson);
lessonRanges.put(lesson.startTime.getValue(), lesson.endTime.getValue());
if (minTime == null || lesson.startTime.getValue() < minTime.getValue()) {
minTime = lesson.startTime;
}
if (maxTime == null || lesson.endTime.getValue() > maxTime.getValue()) {
maxTime = lesson.endTime;
}
}
if (minTime != null) {
d(TAG, "Min time "+minTime.getValue()+" max time "+maxTime.getValue());
Time diff = Time.diff(maxTime, minTime);
int minutes = diff.hour*60+diff.minute;
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
int imgWidth = WIDTH_CONSTANT + maxWeekDay * WIDTH_WEEKDAY + (maxWeekDay-1) * WIDTH_SPACING;
int imgHeight = HEIGHT_PROFILE_NAME + HEIGHT_CONSTANT + minutes*HEIGHT_MINUTE + HEIGHT_FOOTER;
Bitmap bmp = Bitmap.createBitmap(imgWidth+20, imgHeight+30, conf); // this creates a MUTABLE bitmap
Canvas canvas = new Canvas(bmp);
if (noColors)
canvas.drawARGB(255, 255, 255, 255);
else
canvas.drawARGB(255, 225, 225, 225);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
for (LessonFull lesson: lessonList) {
Time lessonLength = Time.diff(lesson.endTime, lesson.startTime);
Time firstOffset = Time.diff(lesson.startTime, minTime);
int left = WIDTH_CONSTANT + lesson.weekDay*WIDTH_WEEKDAY + lesson.weekDay * WIDTH_SPACING;
int top = HEIGHT_PROFILE_NAME + HEIGHT_CONSTANT + (firstOffset.hour*60+firstOffset.minute)*HEIGHT_MINUTE;
int blockWidth = WIDTH_WEEKDAY;
int blockHeight = (lessonLength.hour*60+lessonLength.minute)*HEIGHT_MINUTE;
int viewWidth = Utils.dpToPx(380);
int viewHeight = Utils.dpToPx((lessonLength.hour*60+lessonLength.minute)*4);
LinearLayout linearLayout;
try {
linearLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.row_timetable_block_item, null);
}
catch (Exception e) {
app.apiEdziennik.guiReportException(activity, 385, e);
return;
}
LinearLayout layout = linearLayout.findViewById(R.id.timetableItemLayout);
CardView card = linearLayout.findViewById(R.id.timetableItemCard);
TextView subjectName = linearLayout.findViewById(R.id.timetableItemSubjectName);
TextView classroomName = linearLayout.findViewById(R.id.timetableItemClassroomName);
TextView teacherName = linearLayout.findViewById(R.id.timetableItemTeacherName);
TextView teamName = linearLayout.findViewById(R.id.timetableItemTeamName);
if (noColors) {
card.setCardBackgroundColor(0xffffffff);
card.setCardElevation(0.0f);
layout.setBackgroundResource(R.drawable.bg_rounded_16dp_outline);
subjectName.setTextColor(0xff000000);
classroomName.setTextColor(0xffaaaaaa);
teacherName.setTextColor(0xffaaaaaa);
teamName.setTextColor(0xffaaaaaa);
}
subjectName.setText(lesson.subjectLongName);
classroomName.setText(bs(lesson.classroomName));
teacherName.setText(bs(lesson.teacherFullName));
teamName.setText(bs(lesson.teamName));
if (markChanges) {
if (lesson.changeId != 0) {
if (lesson.changeType == TYPE_CANCELLED) {
card.setCardBackgroundColor(Color.BLACK);
subjectName.setText(Html.fromHtml("<del>"+lesson.subjectLongName+"</del>", null, new SpannableHtmlTagHandler()));
}
else if (lesson.changeType == TYPE_CHANGE) {
card.setCardBackgroundColor(0xff234158); // 0x40 x primary
subjectName.setTypeface(null, Typeface.BOLD_ITALIC);
}
}
}
linearLayout.setDrawingCacheEnabled(true);
linearLayout.measure(View.MeasureSpec.makeMeasureSpec(viewWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(viewHeight, View.MeasureSpec.EXACTLY));
linearLayout.layout(0, 0, linearLayout.getMeasuredWidth(), linearLayout.getMeasuredHeight());
linearLayout.buildDrawingCache(true);
Bitmap bm = linearLayout.getDrawingCache();
canvas.drawBitmap(bm, null, new Rect(left, top, left+blockWidth, top+blockHeight), paint);
}
Paint textPaint = new Paint();
textPaint.setARGB(255, 0, 0, 0);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(30);
textPaint.setAntiAlias(true);
textPaint.setFilterBitmap(true);
textPaint.setDither(true);
for (int w = 0; w < maxWeekDay+1; w++) {
int x = WIDTH_CONSTANT + w*WIDTH_WEEKDAY + w * WIDTH_SPACING;
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY/2), HEIGHT_PROFILE_NAME + HEIGHT_CONSTANT/2+10, textPaint);
}
if (showProfileName) {
textPaint.setTextSize(50);
if (weekStart != null && weekEnd != null) {
canvas.drawText(app.profile.getName() + " - plan lekcji, "+weekStart.getFormattedStringShort() + " - " + weekEnd.getFormattedStringShort(), (imgWidth + 20) / 2, 80, textPaint);
}
else {
canvas.drawText(app.profile.getName() + " - plan lekcji", (imgWidth + 20) / 2, 80, textPaint);
}
}
textPaint.setARGB(128, 0, 0, 0);
textPaint.setTextAlign(Paint.Align.RIGHT);
textPaint.setTextSize(26);
textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC));
textPaint.setAntiAlias(true);
textPaint.setFilterBitmap(true);
textPaint.setDither(true);
int textPaintCenter = Math.round((textPaint.descent() + textPaint.ascent()) / 2);
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imgWidth - 10, imgHeight - textPaintCenter - 10, textPaint);
textPaint.setARGB(255, 127, 127, 127);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(16);
textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
textPaint.setAntiAlias(true);
textPaint.setFilterBitmap(true);
textPaint.setDither(true);
textPaintCenter = Math.round((textPaint.descent() + textPaint.ascent()) / 2); // it's probably negative
Paint linePaint = new Paint();
linePaint.setARGB(255, 100, 100, 100);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setPathEffect(new DashPathEffect(new float[]{10, 10}, 0));
linePaint.setAntiAlias(true);
linePaint.setFilterBitmap(true);
linePaint.setDither(true);
int minTimeVal = minTime.getValue();
int minTimeInt = (minTimeVal/10000)*60 + ((minTimeVal/100)%100);
for (int startTime: lessonRanges.keySet()) {
Integer endTime = lessonRanges.get(startTime);
if (endTime == null)
continue;
int hour = startTime/10000;
int minute = (startTime/100)%100;
int firstOffset = hour * 60 + minute - minTimeInt; // offset in minutes
int top = HEIGHT_PROFILE_NAME + HEIGHT_CONSTANT + firstOffset*HEIGHT_MINUTE;
String text = hour+":"+(minute < 10 ? "0"+minute : minute);
canvas.drawText(text, WIDTH_CONSTANT/2, top-textPaintCenter, textPaint);
canvas.drawLine(WIDTH_CONSTANT, top, imgWidth, top, linePaint);
hour = endTime/10000;
minute = (endTime/100)%100;
firstOffset = hour * 60 + minute - minTimeInt; // offset in minutes
top = HEIGHT_PROFILE_NAME + HEIGHT_CONSTANT + firstOffset*HEIGHT_MINUTE;
text = hour+":"+(minute < 10 ? "0"+minute : minute);
canvas.drawText(text, WIDTH_CONSTANT/2, top-textPaintCenter, textPaint);
canvas.drawLine(WIDTH_CONSTANT, top, imgWidth, top, linePaint);
}
File outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu");
outputDir.mkdirs();
File outputFile = new File(outputDir, "plan_lekcji_"+app.profile.getName()+"_"+Date.getToday().getStringY_m_d()+"_"+Time.getNow().getStringH_M()+".png");
FileOutputStream fos;
try {
fos = new FileOutputStream(outputFile);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Exception e) {
Log.e("SAVE_IMAGE", e.getMessage(), e);
return;
}
activity.runOnUiThread(() -> {
if (progressDialog != null)
progressDialog.dismiss();
new MaterialDialog.Builder(activity)
.title(R.string.timetable_generate_success_title)
.content(R.string.timetable_generate_success_text)
.positiveText(R.string.share)
.negativeText(R.string.open)
.neutralText(R.string.do_nothing)
.onPositive(((dialog, which) -> {
Uri uri = Uri.parse("file://" + outputFile.getAbsolutePath());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(activity, app.getPackageName() + ".provider", outputFile);
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(null, "image/*");
intent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, getString(R.string.share_intent)));
}))
.onNegative(((dialog, which) -> {
Uri uri = Uri.parse("file://" + outputFile.getAbsolutePath());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(activity, app.getPackageName() + ".provider", outputFile);
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
}))
.show();
});
}
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public Adapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}

View File

@ -0,0 +1,201 @@
package pl.szczodrzynski.edziennik.ui.modules.timetable;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Typeface;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
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.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.datamodels.EventFull;
import pl.szczodrzynski.edziennik.datamodels.LessonChange;
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.SpannableHtmlTagHandler;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
public class TimetableAdapter extends RecyclerView.Adapter<TimetableAdapter.ViewHolder> {
private static final String TAG = "TimetableAdapter";
private Context context;
private Date lessonDate;
private List<LessonFull> lessonList;
private List<EventFull> eventList;
public boolean setAsRead = false;
//getting the context and product list with constructor
public TimetableAdapter(Context mCtx, Date lessonDate, List<LessonFull> lessonList, List<EventFull> eventList) {
this.context = mCtx;
this.lessonDate = lessonDate;
this.lessonList = lessonList;
this.eventList = eventList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_timetable_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
App app = (App) context.getApplicationContext();
LessonFull lesson = lessonList.get(position);
holder.timetableItemStartTime.setText(lesson.startTime.getStringHM());
holder.timetableItemEndTime.setText(lesson.endTime.getStringHM());
holder.timetableItemSubjectName.setText(lesson.subjectLongName);
holder.timetableItemClassroomName.setText(lesson.getClassroomName());
holder.timetableItemTeamName.setText(lesson.getTeamName());
holder.timetableItemTeacherName.setText(lesson.getTeacherFullName());
holder.timetableItemCard.setCardBackgroundColor(Utils.getAttr(context, R.attr.colorSurface));
if (lesson.changeId != 0)
{
if (!lesson.seen) {
holder.timetableItemSubjectName.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_4dp));
holder.timetableItemSubjectName.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
AsyncTask.execute(() -> app.db.metadataDao().setSeen(App.profileId, lesson, true));
}
if (lesson.changeType == LessonChange.TYPE_CANCELLED)
{
holder.timetableItemSubjectName.setTypeface(null, Typeface.NORMAL);
holder.timetableItemSubjectChange.setVisibility(View.GONE);
holder.timetableItemSubjectName.setText(Html.fromHtml("<del>"+lesson.subjectLongName+"</del>", null, new SpannableHtmlTagHandler()));
}
else if (lesson.changeType == LessonChange.TYPE_CHANGE)
{
holder.timetableItemSubjectName.setTypeface(null, Typeface.BOLD_ITALIC);
if (lesson.changedSubjectLongName()) {
holder.timetableItemSubjectChange.setText(lesson.subjectLongName);
holder.timetableItemSubjectName.setText(lesson.changeSubjectLongName);
holder.timetableItemSubjectChange.setVisibility(View.VISIBLE);
holder.timetableItemSubjectChange.setText(Html.fromHtml("<del>"+lesson.subjectLongName+"</del>", null, new SpannableHtmlTagHandler()));
}
else {
holder.timetableItemSubjectChange.setVisibility(View.GONE);
}
holder.timetableItemTeacherName.setText(lesson.getTeacherFullName(true));
holder.timetableItemTeamName.setText(lesson.getTeamName(true));
holder.timetableItemClassroomName.setText(lesson.getClassroomName(true));
}
else if (lesson.changeType == LessonChange.TYPE_ADDED)
{
}
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
if (lesson.changeType == LessonChange.TYPE_CANCELLED) {
holder.timetableItemCard.setCardBackgroundColor(Themes.INSTANCE.isDark() ? 0x60000000 : 0xffeeeeee);
}
else if (lesson.changeType == LessonChange.TYPE_CHANGE) {
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(Utils.getAttr(context, R.attr.colorPrimary), PorterDuff.Mode.SRC_ATOP));//.setBackgroundColor(App.getAttr(context, R.attr.cardBackgroundHighlight));
}
}
else
{
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
holder.timetableItemSubjectName.setBackground(null);
holder.timetableItemSubjectName.setTypeface(null, Typeface.NORMAL);
holder.timetableItemSubjectChange.setVisibility(View.GONE);
}
int eventCount = 0;
for (EventFull event: eventList) {
if (event.eventDate.getValue() == lessonDate.getValue()
&& event.startTime != null
&& event.startTime.getValue() == lesson.startTime.getValue()) {
eventCount++;
if (eventCount == 1) {
holder.timetableItemEvent1.setVisibility(View.VISIBLE);
if (event.type == TYPE_HOMEWORK)
holder.timetableItemEvent1.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
else
holder.timetableItemEvent1.setBackgroundColor(event.getColor());
}
else if (eventCount == 2) {
holder.timetableItemEvent2.setVisibility(View.VISIBLE);
if (event.type == TYPE_HOMEWORK)
holder.timetableItemEvent2.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
else
holder.timetableItemEvent2.setBackgroundColor(event.getColor());
}
else if (eventCount == 3) {
holder.timetableItemEvent3.setVisibility(View.VISIBLE);
if (event.type == TYPE_HOMEWORK)
holder.timetableItemEvent3.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
else
holder.timetableItemEvent3.setBackgroundColor(event.getColor());
}
}
}
holder.timetableItemCard.setOnClickListener(v -> new EventListDialog(context).show(app, lessonDate, lesson.startTime));
}
@Override
public int getItemCount() {
return lessonList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
CardView timetableItemCard;
ConstraintLayout timetableItemLayout;
TextView timetableItemStartTime;
TextView timetableItemClassroomName;
TextView timetableItemTeamName;
TextView timetableItemSubjectChange;
TextView timetableItemSubjectName;
TextView timetableItemTeacherName;
TextView timetableItemEndTime;
View timetableItemEvent1;
View timetableItemEvent2;
View timetableItemEvent3;
ViewHolder(View itemView) {
super(itemView);
timetableItemCard = itemView.findViewById(R.id.timetableItemCard);
timetableItemLayout = itemView.findViewById(R.id.timetableItemLayout);
timetableItemStartTime = itemView.findViewById(R.id.timetableItemStartTime);
timetableItemClassroomName = itemView.findViewById(R.id.timetableItemClassroomName);
timetableItemTeamName = itemView.findViewById(R.id.timetableItemTeamName);
timetableItemSubjectChange = itemView.findViewById(R.id.timetableItemSubjectChange);
timetableItemSubjectName = itemView.findViewById(R.id.timetableItemSubjectName);
timetableItemTeacherName = itemView.findViewById(R.id.noticesItemTeacherName);
timetableItemEndTime = itemView.findViewById(R.id.timetableItemEndTime);
timetableItemEvent1 = itemView.findViewById(R.id.timetableItemEvent1);
timetableItemEvent2 = itemView.findViewById(R.id.timetableItemEvent2);
timetableItemEvent3 = itemView.findViewById(R.id.timetableItemEvent3);
}
}
}

View File

@ -0,0 +1,75 @@
package pl.szczodrzynski.edziennik.ui.modules.webpush;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.zxing.Result;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
import pl.szczodrzynski.edziennik.R;
public class QrScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
private ZXingScannerView mScannerView;
public static ZXingScannerView.ResultHandler resultHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
setContentView(mScannerView);
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (result == PackageManager.PERMISSION_GRANTED) {
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera(); // Start camera on resume
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
}
}
@Override
public void onResume() {
super.onResume();
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera(); // Start camera on resume
}
@Override
public void onPause() {
super.onPause();
mScannerView.stopCamera(); // Stop camera on pause
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mScannerView.startCamera();
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_SHORT).show();
}
}
// other 'case' lines to check for other
// permissions this app might request
}
}
@Override
public void handleResult(Result rawResult) {
if (resultHandler != null) {
resultHandler.handleResult(rawResult);
}
finish();
}
}

View File

@ -0,0 +1,299 @@
package pl.szczodrzynski.edziennik.ui.modules.webpush;
import android.Manifest;
import android.content.pm.PackageManager;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;
import java.util.ArrayList;
import java.util.List;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.ActivityWebPushConfigBinding;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
public class WebPushConfigActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
private static final String TAG = "WebPushConfigActivity";
private ZXingScannerView mScannerView;
ActivityWebPushConfigBinding b;
boolean cameraRunning = false;
private void showCamera() {
cameraRunning = true;
Anim.fadeIn(b.qrCodeScanner, 500, null);
b.webPushConfig.setVisibility(View.GONE);
b.qrCodeScanner.startCamera();
b.qrCodeScanner.setResultHandler(this);
}
private App app;
private void hideCamera() {
cameraRunning = false;
Anim.fadeOut(b.qrCodeScanner, 500, null);
b.webPushConfig.setVisibility(View.VISIBLE);
b.qrCodeScanner.stopCamera();
}
private void getPairedBrowsers(@NonNull String newFcm, int removeId) {
Anim.fadeIn(b.browserListProgressBar, 500, null);
Anim.fadeOut(b.browserList, 500, null);
Anim.fadeOut(b.browserListErrorText, 500, null);
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?web_push_list"+(!newFcm.equals("") ? "&web_push_pair" : "") + (removeId != -1 ? "&web_push_unpair" : ""), "WebPushConfigActivity", app.profile)
.setBodyParameter((removeId != -1 ? "id" : "browser_fcm"), (removeId != -1 ? Integer.toString(removeId) : newFcm))
.run(((e, result) -> {
new Handler(Looper.getMainLooper()).post(() -> {
Anim.fadeOut(b.browserListProgressBar, 500, null);
if (result == null || result.get("browser_count") == null) {
b.browserListErrorText.setText(R.string.web_push_connection_error);
Anim.fadeIn(b.browserListErrorText, 500, null);
return;
}
if (result.get("browser_count").getAsInt() == 0) {
b.browserListErrorText.setText(R.string.web_push_no_browsers);
Anim.fadeIn(b.browserListErrorText, 500, null);
if (app.appConfig.webPushEnabled) {
app.appConfig.webPushEnabled = false;
app.appConfig.savePending = true;
}
return;
}
b.browserList.removeAllViews();
LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
textViewParams.setMargins(0, 0, Utils.dpToPx(8), 0);
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams tableRowParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
JsonArray browsers = result.get("browsers").getAsJsonArray();
for (JsonElement browserEl: browsers) {
JsonObject browser = browserEl.getAsJsonObject();
if (browser != null) {
//Log.d(TAG, browser.toString());
String browserDescription = "(error)";
if (browser.get("description") != null) {
browserDescription = browser.get("description").getAsString();
}
int browserId = -1;
if (browser.get("id") != null) {
browserId = browser.get("id").getAsInt();
}
TableRow browserRow = new TableRow(this);
browserRow.setLayoutParams(tableRowParams);
TextView browserDescriptionText = new TextView(this);
//browserDescriptionText.setLayoutParams(textViewParams);
browserDescriptionText.setText(browserDescription);
browserDescriptionText.setGravity(Gravity.CENTER_VERTICAL);
browserRow.addView(browserDescriptionText);
Button browserRemoveButton = new Button(this, null, android.R.attr.buttonStyleSmall);
browserRemoveButton.setMinHeight(0);
browserRemoveButton.setText(R.string.remove);
int finalBrowserId = browserId;
browserRemoveButton.setOnClickListener((v -> {
new MaterialDialog.Builder(this)
.title(R.string.are_you_sure)
.content(R.string.web_push_really_remove)
.positiveText(R.string.yes)
.negativeText(R.string.no)
.onPositive(((dialog, which) -> getPairedBrowsers("", finalBrowserId)))
.show();
}));
browserRow.addView(browserRemoveButton/*, buttonParams*/);
b.browserList.addView(browserRow);
}
}
if (!app.appConfig.webPushEnabled) {
app.appConfig.webPushEnabled = true;
app.appConfig.savePending = true;
}
Anim.fadeIn(b.browserList, 500, null);
});
}));
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showCamera();
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_SHORT).show();
}
}
// other 'case' lines to check for other
// permissions this app might request
}
}
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
app = (App) getApplicationContext();
getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_web_push_config, null, false);
setContentView(b.getRoot());
Toolbar toolbar = b.toolbar;
toolbar.setTitle(R.string.settings_notification_web_push);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
mScannerView = b.qrCodeScanner;
List<BarcodeFormat> formats = new ArrayList<>();
formats.add(BarcodeFormat.QR_CODE);
mScannerView.setFormats(formats);
mScannerView.setAspectTolerance(0.5f);
b.webPushScanNewButton.setOnClickListener((v -> {
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (result == PackageManager.PERMISSION_GRANTED) {
showCamera();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
}
}));
if (app.profile.getRegistration() != REGISTRATION_ENABLED) {
new MaterialDialog.Builder(this)
.title(R.string.web_push_unavailable)
.content(R.string.web_push_you_need_to_register)
.positiveText(R.string.ok)
.negativeText(R.string.what_is_this)
.onPositive(((dialog, which) -> {
dialog.dismiss();
finish();
}))
.onNegative(((dialog, which) -> {
new MaterialDialog.Builder(this)
.title(R.string.help)
.content(R.string.help_notification_web_push)
.positiveText(R.string.ok)
.show();
}))
.dismissListener((dialog -> finish()))
.autoDismiss(false)
.canceledOnTouchOutside(false)
.show();
b.webPushScanNewButton.setEnabled(false);
}
else {
getPairedBrowsers("", -1);
}
}
@Override
public void onResume() {
super.onResume();
// Register ourselves as a handler for scan results.
//mScannerView.startCamera(); // Start camera on resume
//showCamera();
}
@Override
public void onPause() {
super.onPause();
hideCamera();
}
@Override
public void handleResult(Result rawResult) {
// Do something with the result here
//Log.v(TAG, rawResult.getText()); // Prints scan results
//Log.v(TAG, rawResult.getBarcodeFormat().toString()); // Prints the scan format (qrcode, pdf417 etc.)
//Toast.makeText(this, rawResult.getText(), Toast.LENGTH_SHORT).show();
getPairedBrowsers(rawResult.getText(), -1);
/*Ion.with(app.getContext())
.load(app.requestScheme + APP_URL + "main.php?web_push_pair")
.setBodyParameter("username", (app.profile.autoRegistrationAllowed ? app.profile.registrationUsername : app.appConfig.deviceId))
.setBodyParameter("app_version_build_type", BuildConfig.BUILD_TYPE)
.setBodyParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE))
.setBodyParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")")
.setBodyParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.setBodyParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.setBodyParameter("device_os_version", Build.VERSION.RELEASE)
.setBodyParameter("fcm_token", app.appConfig.fcmToken)
.setBodyParameter("browser_fcm", rawResult.getText())
.asJsonObject()
.setCallback((e, result) -> {
getPairedBrowsers(-1);
});*/
hideCamera();
// If you would like to resume scanning, call this method below:
//mScannerView.resumeCameraPreview(this);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
if (cameraRunning) {
hideCamera();
return super.onOptionsItemSelected(item);
}
finish();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
if (cameraRunning) {
hideCamera();
return;
}
super.onBackPressed();
}
}