1
0

Compare commits

...

8 Commits
0.2.0 ... 0.2.2

Author SHA1 Message Date
cdd3d6a53b Version 0.2.2 2018-03-22 22:54:11 +01:00
8431661d54 Fix resuming states in fragments (#72)
* Downgrade sdk version
* Update dependencies
* Fix infinite loading on restore states
* Fix crash when loading loaded fragments
2018-03-22 22:42:08 +01:00
3dabb11473 [API] Add error messages to exceptions (#71) 2018-03-20 23:10:20 +01:00
63b4ed42ca Version 0.2.1 2018-03-14 22:08:57 +01:00
2bb2341d0f Fix display items on refresh (#70) 2018-03-14 21:56:39 +01:00
797e233809 Fix inactive elements (#69) 2018-03-14 20:22:31 +01:00
64b964ca18 Fix circleci instrumented job (#68) 2018-03-13 22:30:33 +01:00
c9bdac6a66 Fix session handling (#67) 2018-03-13 18:19:04 +01:00
39 changed files with 304 additions and 266 deletions

View File

@ -126,6 +126,9 @@ jobs:
name: Launch emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on
background: true
- run:
name: Change circle-android script file permissions
command: sudo chmod +rx /bin/circle-android
- run:
name: Wait emulator
command: |

View File

@ -28,12 +28,12 @@ jacocoTestReport {
}
dependencies {
implementation 'org.jsoup:jsoup:1.10.3'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'com.google.code.gson:gson:2.8.2'
implementation "org.jsoup:jsoup:$jsoup"
implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "com.google.code.gson:gson:$gson"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.13.0'
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"
}
version = PUBLISH_VERSION

View File

@ -108,7 +108,11 @@ public class Client {
this.cookies.addItems(response.cookies());
return checkForErrors(response.parse());
Document doc = checkForErrors(response.parse());
lastSuccessRequest = new Date();
return doc;
}
public Document postPageByUrl(String url, String[][] params) throws IOException, VulcanException {
@ -165,16 +169,16 @@ public class Client {
}
Document checkForErrors(Document doc) throws VulcanException {
if ("Przerwa techniczna".equals(doc.select("title").text())) {
throw new VulcanOfflineException();
String title = doc.select("title").text();
if ("Przerwa techniczna".equals(title)) {
throw new VulcanOfflineException(title);
}
if ("Zaloguj się".equals(doc.select(".loginButton").text())) {
throw new NotLoggedInErrorException();
String singIn = doc.select(".loginButton").text();
if ("Zaloguj się".equals(singIn)) {
throw new NotLoggedInErrorException(singIn);
}
lastSuccessRequest = new Date();
return doc;
}
}

View File

@ -1,4 +1,8 @@
package io.github.wulkanowy.api;
public class NotLoggedInErrorException extends VulcanException {
public NotLoggedInErrorException(String message) {
super(message);
}
}

View File

@ -48,7 +48,7 @@ public class StudentAndParent implements SnP {
Element studentTileLink = startPage.select(".panel.linkownia.pracownik.klient > a").first();
if (null == studentTileLink) {
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
}
String snpPageUrl = studentTileLink.attr("href");
@ -62,7 +62,7 @@ public class StudentAndParent implements SnP {
String[] path = snpPageUrl.split(client.getHost())[1].split("/");
if (5 != path.length) {
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in");
}
return path[2];

View File

@ -32,7 +32,7 @@ public class Vulcan {
public Client getClient() throws NotLoggedInErrorException {
if (null == client) {
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("Use setCredentials() method first");
}
return client;

View File

@ -1,4 +1,8 @@
package io.github.wulkanowy.api;
public abstract class VulcanException extends Exception {
protected VulcanException(String message) {
super(message);
}
}

View File

@ -1,4 +1,8 @@
package io.github.wulkanowy.api;
public class VulcanOfflineException extends VulcanException {
VulcanOfflineException(String message) {
super(message);
}
}

View File

@ -3,4 +3,8 @@ package io.github.wulkanowy.api.login;
import io.github.wulkanowy.api.VulcanException;
public class AccountPermissionException extends VulcanException {
AccountPermissionException(String message) {
super(message);
}
}

View File

@ -3,4 +3,8 @@ package io.github.wulkanowy.api.login;
import io.github.wulkanowy.api.VulcanException;
public class BadCredentialsException extends VulcanException {
BadCredentialsException(String message) {
super(message);
}
}

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.api.login;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
@ -42,8 +43,9 @@ public class Login {
{"Password", password}
});
if (null != html.select(".ErrorMessage").first()) {
throw new BadCredentialsException();
Element errorMessage = html.select(".ErrorMessage").first();
if (null != errorMessage) {
throw new BadCredentialsException(errorMessage.text());
}
return html.select("input[name=wresult]").attr("value");
@ -59,11 +61,11 @@ public class Login {
}).select("title").text();
if ("Logowanie".equals(title)) {
throw new AccountPermissionException();
throw new AccountPermissionException("No account access. Try another symbol");
}
if (!"Uonet+".equals(title)) {
throw new LoginErrorException();
throw new LoginErrorException("Could not log in, unknown error");
}
return this.symbol;

View File

@ -2,5 +2,9 @@ package io.github.wulkanowy.api.login;
import io.github.wulkanowy.api.NotLoggedInErrorException;
public class LoginErrorException extends NotLoggedInErrorException {
class LoginErrorException extends NotLoggedInErrorException {
LoginErrorException(String message) {
super(message);
}
}

View File

@ -3,4 +3,8 @@ package io.github.wulkanowy.api.messages;
import io.github.wulkanowy.api.VulcanException;
class BadRequestException extends VulcanException {
BadRequestException(String message) {
super(message);
}
}

View File

@ -59,10 +59,10 @@ public class Messages {
messages = new Gson().fromJson(res, MessagesContainer.class).data;
} catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) {
throw new BadRequestException();
throw new BadRequestException(ERROR_TITLE);
}
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in");
}
return messages;
@ -80,10 +80,10 @@ public class Messages {
message = new Gson().fromJson(res, MessageContainer.class).data;
} catch (JsonParseException e) {
if (res.contains(ERROR_TITLE)) {
throw new BadRequestException();
throw new BadRequestException(ERROR_TITLE);
}
throw new NotLoggedInErrorException();
throw new NotLoggedInErrorException("You are probably not logged in. Force login");
}
return message;

View File

@ -21,15 +21,15 @@ apply from: '../jacoco.gradle'
apply from: '../android-sonarqube.gradle'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
compileSdkVersion 26
buildToolsVersion "26.0.3"
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 27
versionCode 3
versionName "0.2.0"
targetSdkVersion 26
versionCode 5
versionName "0.2.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [
@ -68,42 +68,39 @@ greendao {
dependencies {
implementation project(':api')
implementation 'com.android.support:appcompat-v7:27.1.0'
implementation 'com.android.support:design:27.1.0'
implementation 'com.android.support:support-v4:27.1.0'
implementation 'com.android.support:recyclerview-v7:27.1.0'
implementation 'com.android.support:cardview-v7:27.1.0'
implementation 'com.android.support:customtabs:27.1.0'
implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
implementation 'org.apache.commons:commons-lang3:3.7'
implementation 'eu.davidea:flexible-adapter:5.0.0-rc4'
implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b1'
implementation 'org.apache.commons:commons-collections4:4.1'
implementation 'org.greenrobot:greendao:3.2.2'
implementation 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.0.2'
implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'joda-time:joda-time:2.9.9'
implementation 'com.google.dagger:dagger-android:2.14.1'
implementation 'com.google.dagger:dagger-android-support:2.14.1'
implementation 'com.aurelhubert:ahbottomnavigation:2.1.0'
implementation "com.android.support:support-v4:$supportVersion"
implementation "com.android.support:design:$supportVersion"
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:customtabs:$supportVersion"
implementation "com.firebase:firebase-jobdispatcher:$firebaseJob"
implementation "org.apache.commons:commons-lang3:$apacheLang"
implementation "org.apache.commons:commons-collections4:$apacheCollections"
implementation "eu.davidea:flexible-adapter:$flexibleAdapter"
implementation "eu.davidea:flexible-adapter-ui:$flexibleUi"
implementation "org.greenrobot:greendao:$greenDao"
implementation "com.github.yuweiguocn:GreenDaoUpgradeHelper:$greenDaoHelper"
implementation "com.jakewharton:butterknife:$butterknife"
implementation "joda-time:joda-time:$jodaTime"
implementation "com.google.dagger:dagger-android-support:$dagger2"
implementation "com.aurelhubert:ahbottomnavigation:$ahbottom"
implementation('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsSdk@aar") {
transitive = true
}
implementation('com.crashlytics.sdk.android:answers:1.4.1@aar') {
implementation("com.crashlytics.sdk.android:answers:$crashlyticsAnswers@aar") {
transitive = true
}
annotationProcessor 'com.google.dagger:dagger-android-processor:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
annotationProcessor "com.google.dagger:dagger-android-processor:$dagger2"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger2"
annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife"
debugImplementation 'com.amitshekhar.android:debug-db:1.0.1'
debugImplementation 'net.zetetic:android-database-sqlcipher:3.5.9'
debugImplementation "com.amitshekhar.android:debug-db:$debugDb"
debugImplementation "net.zetetic:android-database-sqlcipher:$sqlcipher"
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.13.0'
testImplementation "junit:junit:$junit"
testImplementation "org.mockito:mockito-core:$mockito"
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'org.mockito:mockito-android:2.13.0'
androidTestImplementation "com.android.support.test:runner:$testRunner"
androidTestImplementation "org.mockito:mockito-android:$mockito"
}

View File

@ -16,6 +16,7 @@ import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.di.component.ApplicationComponent;
import io.github.wulkanowy.di.component.DaggerApplicationComponent;
import io.github.wulkanowy.di.modules.ApplicationModule;
import io.github.wulkanowy.utils.LogUtils;
public class WulkanowyApp extends Application {
@ -37,6 +38,17 @@ public class WulkanowyApp extends Application {
enableDebugLog();
}
initializeFabric();
initializeUserSession();
}
private void initializeUserSession() {
if (repository.getCurrentUserId() != 0) {
try {
repository.initLastUser();
} catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e);
}
}
}
private void enableDebugLog() {

View File

@ -12,7 +12,6 @@ import javax.inject.Singleton;
import io.github.wulkanowy.R;
import io.github.wulkanowy.api.NotLoggedInErrorException;
import io.github.wulkanowy.api.VulcanOfflineException;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
import io.github.wulkanowy.di.annotations.ApplicationContext;
import io.github.wulkanowy.utils.AppConstant;
@ -51,8 +50,6 @@ public class AppResources implements ResourcesContract {
return resources.getString(R.string.generic_timeout_error);
} else if (exception instanceof NotLoggedInErrorException || exception instanceof IOException) {
return resources.getString(R.string.login_denied_text);
} else if (exception instanceof VulcanOfflineException) {
return resources.getString(R.string.error_host_offline);
} else {
return exception.getMessage();
}

View File

@ -17,20 +17,16 @@ public interface AttendanceContract {
void setAdapterWithTabLayout();
void setChildFragmentSelected(int position, boolean selected);
boolean isMenuVisible();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentVisible(boolean isVisible);
void onTabSelected(int position);
void onTabUnselected(int position);
void onFragmentActivated(boolean isVisible);
void onStart(View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position);
}
}

View File

@ -20,7 +20,9 @@ import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View, TabLayout.OnTabSelectedListener {
public class AttendanceFragment extends BaseFragment implements AttendanceContract.View {
private static final String CURRENT_ITEM_KEY = "CurrentItem";
@BindView(R.id.attendance_fragment_viewpager)
ViewPager viewPager;
@ -45,6 +47,10 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
}
return view;
@ -54,25 +60,10 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
presenter.onFragmentActivated(menuVisible);
}
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
presenter.onTabSelected(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
presenter.onTabUnselected(tab.getPosition());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
@Override
public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData);
@ -81,14 +72,7 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
@Override
public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
}
@Override
public void setChildFragmentSelected(int position, boolean selected) {
((AttendanceTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
}
@Override
@ -109,6 +93,12 @@ public class AttendanceFragment extends BaseFragment implements AttendanceContra
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
presenter.onDestroy();

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.ui.main.attendance;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@ -16,6 +19,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day;
@ -55,12 +59,13 @@ public class AttendanceHeaderItem
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder,
int position, List<Object> payloads) {
holder.onBind(day, getSubItems());
}
@ -87,10 +92,16 @@ public class AttendanceHeaderItem
@BindColor(R.color.free_day)
int backgroundFreeDay;
@BindColor(android.R.color.black)
int black;
private Context context;
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
view.setOnClickListener(this);
ButterKnife.bind(this, view);
context = view.getContext();
}
void onBind(Day item, List<AttendanceSubItem> subItems) {
@ -103,14 +114,30 @@ public class AttendanceHeaderItem
description.setVisibility(numberOfHours > 0 ? View.VISIBLE : View.INVISIBLE);
alert.setVisibility(isSubItemsHasChanges(subItems) ? View.VISIBLE : View.INVISIBLE);
freeName.setVisibility(subItems.isEmpty() ? View.VISIBLE : View.INVISIBLE);
setInactiveHeader(item.getAttendanceLessons().isEmpty());
}
if (item.getAttendanceLessons().isEmpty()) {
((FrameLayout) getContentView()).setForeground(null);
private void setInactiveHeader(boolean inactive) {
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable());
dayName.setTextColor(inactive ? secondaryColor : black);
if (inactive) {
getContentView().setBackgroundColor(backgroundFreeDay);
dayName.setTextColor(secondaryColor);
} else {
getContentView().setBackgroundDrawable(context.getResources()
.getDrawable(R.drawable.ic_border));
}
}
private Drawable getSelectableDrawable() {
int[] attrs = new int[]{R.attr.selectableItemBackground};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
Drawable drawable = typedArray.getDrawable(0);
typedArray.recycle();
return drawable;
}
private int countNotPresentHours(List<AttendanceSubItem> subItems) {
int i = 0;
for (AttendanceSubItem subItem : subItems) {
@ -118,17 +145,16 @@ public class AttendanceHeaderItem
i++;
}
}
return i;
}
private boolean isSubItemsHasChanges(List<AttendanceSubItem> subItems) {
for (AttendanceSubItem subItem : subItems) {
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson().getIsUnexcusedLateness()) {
if (subItem.getLesson().getIsAbsenceUnexcused() || subItem.getLesson()
.getIsUnexcusedLateness()) {
return true;
}
}
return false;
}
}

View File

@ -24,7 +24,7 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
private OnFragmentIsReadyListener listener;
private int positionToScroll;
private int positionToScroll = 0;
private boolean isFirstSight = false;
@ -45,7 +45,10 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
}
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
}
if (!isFirstSight) {
isFirstSight = true;
@ -57,22 +60,12 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
}
@Override
public void onFragmentVisible(boolean isVisible) {
public void onFragmentActivated(boolean isVisible) {
if (isVisible) {
getView().setActivityTitle();
}
}
@Override
public void onTabSelected(int position) {
getView().setChildFragmentSelected(position, true);
}
@Override
public void onTabUnselected(int position) {
getView().setChildFragmentSelected(position, false);
}
@Override
public void onDoInBackgroundLoading() throws Exception {
for (String date : dates) {
@ -97,6 +90,11 @@ public class AttendancePresenter extends BasePresenter<AttendanceContract.View>
}
}
@Override
public void setRestoredPosition(int position) {
this.positionToScroll = position;
}
@Override
public void onDestroy() {
isFirstSight = false;

View File

@ -16,6 +16,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.AttendanceLesson;
@ -60,12 +61,13 @@ class AttendanceSubItem
}
@Override
public AttendanceSubItem.SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
return new AttendanceSubItem.SubItemViewHolder(view, adapter);
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
return new SubItemViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, AttendanceSubItem.SubItemViewHolder holder, int position, List payloads) {
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder,
int position, List<Object> payloads) {
holder.onBind(lesson);
}

View File

@ -21,12 +21,10 @@ public interface AttendanceTabContract {
interface Presenter extends BaseContract.Presenter<AttendanceTabContract.View> {
void onFragmentSelected(boolean isSelected);
void onFragmentActivated(boolean isSelected);
void setArgumentDate(String date);
void onStart(AttendanceTabContract.View view, boolean isPrimary);
void onRefresh();
}
}

View File

@ -27,12 +27,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
private static final String ARGUMENT_KEY = "date";
private static final String SAVED_KEY = "isSelected";
private boolean isPrimary = false;
private boolean isSelected = false;
@BindView(R.id.attendance_tab_fragment_recycler)
RecyclerView recyclerView;
@ -51,6 +45,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Inject
FlexibleAdapter<AttendanceHeaderItem> adapter;
private boolean isFragmentVisible = false;
public static AttendanceTabFragment newInstance(String date) {
AttendanceTabFragment fragmentTab = new AttendanceTabFragment();
@ -61,15 +57,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
return fragmentTab;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -84,7 +71,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this, isPrimary);
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
}
return view;
}
@ -111,10 +99,9 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null && getView() != null) {
presenter.onFragmentSelected(isSelected);
} else if (isSelected) {
isPrimary = true;
isFragmentVisible = menuVisible;
if (presenter != null) {
presenter.onFragmentActivated(menuVisible);
}
}
@ -143,10 +130,6 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override
public void onError(String message) {
if (getActivity() != null) {
@ -155,15 +138,8 @@ public class AttendanceTabFragment extends BaseFragment implements AttendanceTab
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(SAVED_KEY, isSelected);
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
isPrimary = false;
presenter.onDestroy();
super.onDestroyView();
}

View File

@ -33,21 +33,22 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
}
@Override
public void onStart(AttendanceTabContract.View view, boolean isPrimary) {
public void onStart(AttendanceTabContract.View view) {
super.onStart(view);
getView().showProgressBar(true);
getView().showNoItem(false);
onFragmentSelected(isPrimary);
}
@Override
public void onFragmentSelected(boolean isSelected) {
if (!isFirstSight && isSelected) {
public void onFragmentActivated(boolean isSelected) {
if (!isFirstSight && isSelected && isViewAttached()) {
isFirstSight = true;
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
} else if (!isSelected) {
cancelAsyncTasks();
}
}
@ -105,6 +106,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
boolean isEmptyWeek = true;
for (Day day : dayList) {
day.resetAttendanceLessons();
AttendanceHeaderItem headerItem = new AttendanceHeaderItem(day);
if (isEmptyWeek) {
@ -156,10 +158,7 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
getRepository().syncAttendance(date);
}
@Override
public void onDestroy() {
isFirstSight = false;
private void cancelAsyncTasks() {
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
@ -168,7 +167,12 @@ public class AttendanceTabPresenter extends BasePresenter<AttendanceTabContract.
loadingTask.cancel(true);
loadingTask = null;
}
}
@Override
public void onDestroy() {
cancelAsyncTasks();
isFirstSight = false;
super.onDestroy();
}
}

View File

@ -109,6 +109,7 @@ public class GradesPresenter extends BasePresenter<GradesContract.View>
headerItems = new ArrayList<>();
for (Subject subject : subjectList) {
subject.resetGradeList();
List<Grade> gradeList = subject.getGradeList();
if (!gradeList.isEmpty()) {

View File

@ -37,7 +37,7 @@ public class GradesSubItem
return grade;
}
public void setSubjectAlertImage(View subjectAlertImage) {
void setSubjectAlertImage(View subjectAlertImage) {
this.subjectAlertImage = subjectAlertImage;
}

View File

@ -17,20 +17,16 @@ public interface TimetableContract {
void setAdapterWithTabLayout();
void setChildFragmentSelected(int position, boolean selected);
boolean isMenuVisible();
}
@PerActivity
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentVisible(boolean isVisible);
void onTabSelected(int position);
void onTabUnselected(int position);
void onFragmentActivated(boolean isVisible);
void onStart(View view, OnFragmentIsReadyListener listener);
void setRestoredPosition(int position);
}
}

View File

@ -20,7 +20,9 @@ import io.github.wulkanowy.ui.base.BaseFragment;
import io.github.wulkanowy.ui.main.OnFragmentIsReadyListener;
import io.github.wulkanowy.ui.main.TabsData;
public class TimetableFragment extends BaseFragment implements TimetableContract.View, TabLayout.OnTabSelectedListener {
public class TimetableFragment extends BaseFragment implements TimetableContract.View {
private static final String CURRENT_ITEM_KEY = "CurrentItem";
@BindView(R.id.timetable_fragment_viewpager)
ViewPager viewPager;
@ -44,6 +46,10 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
component.inject(this);
setButterKnife(ButterKnife.bind(this, view));
presenter.onStart(this, (OnFragmentIsReadyListener) getActivity());
if (savedInstanceState != null) {
presenter.setRestoredPosition(savedInstanceState.getInt(CURRENT_ITEM_KEY));
}
}
return view;
}
@ -52,25 +58,10 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null) {
presenter.onFragmentVisible(menuVisible);
presenter.onFragmentActivated(menuVisible);
}
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
presenter.onTabSelected(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
presenter.onTabUnselected(tab.getPosition());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
@Override
public void setTabDataToAdapter(TabsData tabsData) {
pagerAdapter.setTabsData(tabsData);
@ -79,14 +70,7 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
@Override
public void setAdapterWithTabLayout() {
viewPager.setAdapter(pagerAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(this);
}
@Override
public void setChildFragmentSelected(int position, boolean selected) {
((TimetableTabFragment) pagerAdapter.getItem(position)).setSelected(selected);
}
@Override
@ -107,6 +91,12 @@ public class TimetableFragment extends BaseFragment implements TimetableContract
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(CURRENT_ITEM_KEY, viewPager.getCurrentItem());
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
presenter.onDestroy();

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.ui.main.timetable;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@ -16,6 +19,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.ExpandableViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.Day;
@ -55,12 +59,13 @@ public class TimetableHeaderItem
}
@Override
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
public HeaderViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
return new HeaderViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, HeaderViewHolder holder,
int position, List<Object> payloads) {
holder.onBind(day, getSubItems());
}
@ -84,10 +89,16 @@ public class TimetableHeaderItem
@BindColor(R.color.free_day)
int backgroundFreeDay;
@BindColor(android.R.color.black)
int black;
private Context context;
HeaderViewHolder(View view, FlexibleAdapter adapter) {
super(view, adapter);
view.setOnClickListener(this);
ButterKnife.bind(this, view);
context = view.getContext();
}
void onBind(Day item, List<TimetableSubItem> subItems) {
@ -96,14 +107,29 @@ public class TimetableHeaderItem
alert.setVisibility(isSubItemNewMovedInOrChanged(subItems) ? View.VISIBLE : View.INVISIBLE);
freeName.setVisibility(item.getIsFreeDay() ? View.VISIBLE : View.INVISIBLE);
freeName.setText(item.getFreeDayName());
setInactiveHeader(item.getIsFreeDay());
}
if (item.getIsFreeDay()) {
((FrameLayout) getContentView()).setForeground(null);
private void setInactiveHeader(boolean inactive) {
((FrameLayout) getContentView()).setForeground(inactive ? null : getSelectableDrawable());
dayName.setTextColor(inactive ? secondaryColor : black);
if (inactive) {
getContentView().setBackgroundColor(backgroundFreeDay);
dayName.setTextColor(secondaryColor);
} else {
getContentView().setBackgroundDrawable(context.getResources()
.getDrawable(R.drawable.ic_border));
}
}
private Drawable getSelectableDrawable() {
int[] attrs = new int[]{R.attr.selectableItemBackground};
TypedArray typedArray = context.obtainStyledAttributes(attrs);
Drawable drawable = typedArray.getDrawable(0);
typedArray.recycle();
return drawable;
}
private boolean isSubItemNewMovedInOrChanged(List<TimetableSubItem> subItems) {
boolean isAlertActive = false;

View File

@ -24,7 +24,7 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
private OnFragmentIsReadyListener listener;
private int positionToScroll;
private int positionToScroll = 0;
private boolean isFirstSight = false;
@ -45,7 +45,10 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
if (dates.isEmpty()) {
dates = TimeUtils.getMondaysFromCurrentSchoolYear();
}
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
if (positionToScroll == 0) {
positionToScroll = dates.indexOf(TimeUtils.getDateOfCurrentMonday(true));
}
if (!isFirstSight) {
isFirstSight = true;
@ -57,22 +60,12 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
}
@Override
public void onFragmentVisible(boolean isVisible) {
public void onFragmentActivated(boolean isVisible) {
if (isVisible) {
getView().setActivityTitle();
}
}
@Override
public void onTabSelected(int position) {
getView().setChildFragmentSelected(position, true);
}
@Override
public void onTabUnselected(int position) {
getView().setChildFragmentSelected(position, false);
}
@Override
public void onDoInBackgroundLoading() throws Exception {
for (String date : dates) {
@ -84,7 +77,6 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
@Override
public void onCanceledLoadingAsync() {
//do nothing
}
@Override
@ -97,6 +89,11 @@ public class TimetablePresenter extends BasePresenter<TimetableContract.View>
}
}
@Override
public void setRestoredPosition(int position) {
this.positionToScroll = position;
}
@Override
public void onDestroy() {
isFirstSight = false;

View File

@ -17,6 +17,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.davidea.flexibleadapter.items.AbstractSectionableItem;
import eu.davidea.flexibleadapter.items.IFlexible;
import eu.davidea.viewholders.FlexibleViewHolder;
import io.github.wulkanowy.R;
import io.github.wulkanowy.data.db.dao.entities.TimetableLesson;
@ -27,7 +28,7 @@ public class TimetableSubItem
private TimetableLesson lesson;
public TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
TimetableSubItem(TimetableHeaderItem header, TimetableLesson lesson) {
super(header);
this.lesson = lesson;
}
@ -62,12 +63,13 @@ public class TimetableSubItem
}
@Override
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
public SubItemViewHolder createViewHolder(View view, FlexibleAdapter<IFlexible> adapter) {
return new SubItemViewHolder(view, adapter);
}
@Override
public void bindViewHolder(FlexibleAdapter adapter, SubItemViewHolder holder, int position, List payloads) {
public void bindViewHolder(FlexibleAdapter<IFlexible> adapter, SubItemViewHolder holder,
int position, List<Object> payloads) {
holder.onBind(lesson);
}

View File

@ -23,12 +23,10 @@ public interface TimetableTabContract {
interface Presenter extends BaseContract.Presenter<View> {
void onFragmentSelected(boolean isSelected);
void onFragmentActivated(boolean isSelected);
void setArgumentDate(String date);
void onStart(View view, boolean isPrimary);
void onRefresh();
}
}

View File

@ -28,12 +28,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
private static final String ARGUMENT_KEY = "date";
private static final String SAVED_KEY = "isSelected";
private boolean isPrimary = false;
private boolean isSelected = false;
@BindView(R.id.timetable_tab_fragment_recycler)
RecyclerView recyclerView;
@ -55,6 +49,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Inject
FlexibleAdapter<TimetableHeaderItem> adapter;
private boolean isFragmentVisible = false;
public static TimetableTabFragment newInstance(String date) {
TimetableTabFragment fragmentTab = new TimetableTabFragment();
@ -65,14 +61,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
return fragmentTab;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
isSelected = savedInstanceState.getBoolean(SAVED_KEY, isSelected);
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -86,8 +74,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
if (getArguments() != null) {
presenter.setArgumentDate(getArguments().getString(ARGUMENT_KEY));
}
presenter.onStart(this, isPrimary);
presenter.onStart(this);
presenter.onFragmentActivated(isFragmentVisible);
}
return view;
}
@ -114,10 +102,9 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
if (presenter != null && getView() != null) {
presenter.onFragmentSelected(isSelected);
} else if (isSelected) {
isPrimary = true;
isFragmentVisible = menuVisible;
if (presenter != null) {
presenter.onFragmentActivated(menuVisible);
}
}
@ -151,10 +138,6 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
noItemView.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override
public void onError(String message) {
if (getActivity() != null) {
@ -163,15 +146,8 @@ public class TimetableTabFragment extends BaseFragment implements TimetableTabCo
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(SAVED_KEY, isSelected);
super.onSaveInstanceState(outState);
}
@Override
public void onDestroyView() {
isPrimary = false;
presenter.onDestroy();
super.onDestroyView();
}

View File

@ -36,21 +36,22 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
}
@Override
public void onStart(TimetableTabContract.View view, boolean isPrimary) {
public void onStart(TimetableTabContract.View view) {
super.onStart(view);
getView().showProgressBar(true);
getView().showNoItem(false);
onFragmentSelected(isPrimary);
}
@Override
public void onFragmentSelected(boolean isSelected) {
if (!isFirstSight && isSelected) {
public void onFragmentActivated(boolean isSelected) {
if (!isFirstSight && isSelected && isViewAttached()) {
isFirstSight = true;
loadingTask = new AbstractTask();
loadingTask.setOnFirstLoadingListener(this);
loadingTask.execute();
} else if (!isSelected) {
cancelAsyncTasks();
}
}
@ -108,6 +109,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
boolean isFreeWeek = true;
for (Day day : dayList) {
day.resetTimetableLessons();
TimetableHeaderItem headerItem = new TimetableHeaderItem(day);
if (isFreeWeek) {
@ -160,10 +162,7 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
getRepository().syncTimetable(date);
}
@Override
public void onDestroy() {
isFirstSight = false;
private void cancelAsyncTasks() {
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
@ -172,6 +171,12 @@ public class TimetableTabPresenter extends BasePresenter<TimetableTabContract.Vi
loadingTask.cancel(true);
loadingTask = null;
}
}
@Override
public void onDestroy() {
isFirstSight = false;
cancelAsyncTasks();
super.onDestroy();
}
}

View File

@ -6,7 +6,6 @@ import javax.inject.Inject;
import io.github.wulkanowy.data.RepositoryContract;
import io.github.wulkanowy.ui.base.BasePresenter;
import io.github.wulkanowy.utils.LogUtils;
public class SplashPresenter extends BasePresenter<SplashContract.View>
implements SplashContract.Presenter {
@ -24,12 +23,7 @@ public class SplashPresenter extends BasePresenter<SplashContract.View>
if (getRepository().getCurrentUserId() == 0) {
getView().openLoginActivity();
} else {
try {
getRepository().initLastUser();
getView().openMainActivity();
} catch (Exception e) {
LogUtils.error("An error occurred when the application was started", e);
}
getView().openMainActivity();
}
}
}

View File

@ -39,7 +39,6 @@
<string name="fragment_no_grades">Brak ocen</string>
<string name="noInternet_text">Brak połączenia z internetem</string>
<string name="error_host_offline">Przerwa techniczna. Spróbuj ponownie później</string>
<string name="encrypt_failed_text">Szyfrowanie nie powiodło się. Automatyczne logowanie zostało wyłączone</string>
<string name="version_text">Wersja %1$s</string>
<string name="refresh_error_text">"Podczas odświeżania zawartości wystąpił błąd. "</string>

View File

@ -73,7 +73,6 @@
<string name="info_average_grades">Average: %1$.2f</string>
<string name="info_no_average">No average</string>
<string name="info_free_week">No lesson in this week</string>
<string name="error_host_offline">Technical break</string>
<string name="timetable_subitem_room">Room %s</string>

View File

@ -7,9 +7,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.1"
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
@ -29,6 +26,31 @@ subprojects {
ext {
GROUP_ID = "io.github.wulkanowy"
supportVersion = "26.1.0"
firebaseJob = "0.8.5"
apacheLang = "3.7"
apacheCollections = "4.1"
flexibleAdapter = "5.0.2"
flexibleUi = "1.0.0-b2"
greenDao = "3.2.2"
greenDaoHelper = "v2.0.2"
butterknife = "8.8.1"
jodaTime = "2.9.9"
dagger2 = "2.15"
ahbottom = "2.1.0"
jsoup = "1.10.3"
gson = "2.8.2"
debugDb = "1.0.3"
sqlcipher = "3.5.9"
junit = "4.12"
mockito = "2.16.0"
testRunner = "1.0.1"
crashlyticsSdk = "2.9.1"
crashlyticsAnswers = "1.4.1"
}
allprojects {