From 882b4d5a7e2272f404ddb4b583889768312f517f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" Date: Wed, 19 Jun 2019 11:34:46 +0530 Subject: [PATCH 01/36] build(deps): bump sentry-android-gradle-plugin from 1.7.22 to 1.7.23 (#1730) Bumps [sentry-android-gradle-plugin](https://github.com/getsentry/sentry-java) from 1.7.22 to 1.7.23. - [Release notes](https://github.com/getsentry/sentry-java/releases) - [Changelog](https://github.com/getsentry/sentry-java/blob/master/CHANGES) - [Commits](https://github.com/getsentry/sentry-java/compare/v1.7.22...v1.7.23) Signed-off-by: dependabot-preview[bot] Co-authored-by: null --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7ca1ea7be..5244e8697 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.4.1' - classpath 'io.sentry:sentry-android-gradle-plugin:1.7.22' + classpath 'io.sentry:sentry-android-gradle-plugin:1.7.23' classpath 'com.noveogroup.android:check:1.2.5' classpath "com.google.gms:oss-licenses:0.9.2" classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.8.1" From 6c5c991fe8363f2773cf1e702f4524853a4fd03f Mon Sep 17 00:00:00 2001 From: Shridhar Goel Date: Wed, 19 Jun 2019 16:51:36 +0530 Subject: [PATCH 02/36] fix: Retain password visibility state on screen rotation (#1731) --- app/src/main/res/layout/change_password_fragment.xml | 3 +++ app/src/main/res/layout/login_fragment.xml | 1 + .../main/res/layout/reset_password_by_token_fragment.xml | 2 ++ app/src/main/res/layout/sign_up_fragment.xml | 6 ++++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/change_password_fragment.xml b/app/src/main/res/layout/change_password_fragment.xml index 5d09a4403..502271a23 100644 --- a/app/src/main/res/layout/change_password_fragment.xml +++ b/app/src/main/res/layout/change_password_fragment.xml @@ -62,6 +62,7 @@ android:paddingStart="@dimen/spacing_normal"> Date: Fri, 21 Jun 2019 03:06:49 +0530 Subject: [PATCH 03/36] fix: Retain state of statistics switches on screen rotation (#1735) --- .../organizer/core/event/dashboard/EventDashboardFragment.java | 2 -- app/src/main/res/layout/event_statistics.xml | 2 +- app/src/main/res/layout/order_statistics.xml | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/eventyay/organizer/core/event/dashboard/EventDashboardFragment.java b/app/src/main/java/com/eventyay/organizer/core/event/dashboard/EventDashboardFragment.java index d35791c2a..e6343ef13 100644 --- a/app/src/main/java/com/eventyay/organizer/core/event/dashboard/EventDashboardFragment.java +++ b/app/src/main/java/com/eventyay/organizer/core/event/dashboard/EventDashboardFragment.java @@ -104,8 +104,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, public void onStart() { super.onStart(); getPresenter().attach(initialEventId, this); - binding.eventStatistics.switchEventStatistics.setChecked(false); - binding.orderStatistics.switchOrderStatistics.setChecked(false); binding.setPresenter(getPresenter()); setupRefreshListener(); getPresenter().start(); diff --git a/app/src/main/res/layout/event_statistics.xml b/app/src/main/res/layout/event_statistics.xml index 4c3de4633..a311ba82b 100644 --- a/app/src/main/res/layout/event_statistics.xml +++ b/app/src/main/res/layout/event_statistics.xml @@ -54,7 +54,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:visibility="gone" + android:visibility="@{ switchEventStatistics.checked ? View.VISIBLE : View.GONE }" android:weightSum="200"> + android:visibility="@{ switchOrderStatistics.checked ? View.VISIBLE : View.GONE }"> Date: Fri, 21 Jun 2019 03:07:12 +0530 Subject: [PATCH 04/36] chore: Migrate AttendeesPresenter to ViewModel (#1733) --- .../common/di/module/ViewModelModule.java | 6 + .../core/attendee/list/AttendeesFragment.java | 33 +- .../attendee/list/AttendeesPresenter.java | 150 -------- .../attendee/list/AttendeesViewModel.java | 163 +++++++++ .../core/attendee/list/SwipeController.java | 8 +- .../core/presenter/AttendeePresenterTest.java | 320 ------------------ 6 files changed, 191 insertions(+), 489 deletions(-) delete mode 100644 app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesPresenter.java create mode 100644 app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesViewModel.java delete mode 100644 app/src/test/java/com/eventyay/organizer/core/presenter/AttendeePresenterTest.java diff --git a/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java b/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java index 08a5ae941..941565a68 100644 --- a/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java +++ b/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java @@ -6,6 +6,7 @@ import com.eventyay.organizer.common.di.OrgaViewModelFactory; import com.eventyay.organizer.core.attendee.checkin.AttendeeCheckInViewModel; import com.eventyay.organizer.core.attendee.history.CheckInHistoryViewModel; +import com.eventyay.organizer.core.attendee.list.AttendeesViewModel; import com.eventyay.organizer.core.attendee.qrscan.ScanQRViewModel; import com.eventyay.organizer.core.auth.login.LoginViewModel; import com.eventyay.organizer.core.auth.reset.ResetPasswordViewModel; @@ -240,6 +241,11 @@ public abstract class ViewModelModule { @ViewModelKey(ScanQRViewModel.class) public abstract ViewModel bindScanQRViewModel(ScanQRViewModel scanQRViewModel); + @Binds + @IntoMap + @ViewModelKey(AttendeesViewModel.class) + public abstract ViewModel bindAttendeesViewModel(AttendeesViewModel attendeesViewModel); + @Binds public abstract ViewModelProvider.Factory bindViewModelFactory(OrgaViewModelFactory factory); diff --git a/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesFragment.java b/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesFragment.java index 0fd4c0247..0e8eeed11 100644 --- a/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesFragment.java +++ b/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesFragment.java @@ -11,6 +11,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProviders; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DividerItemDecoration; @@ -46,8 +48,6 @@ import javax.inject.Inject; -import dagger.Lazy; - /** * A simple {@link Fragment} subclass. * Use the {@link AttendeesFragment#newInstance} factory method to @@ -55,7 +55,7 @@ */ @SuppressWarnings("PMD.TooManyMethods") -public class AttendeesFragment extends BaseFragment implements AttendeesView { +public class AttendeesFragment extends BaseFragment implements AttendeesView { private Context context; @@ -65,7 +65,7 @@ public class AttendeesFragment extends BaseFragment implemen ContextUtils utilModel; @Inject - Lazy presenterProvider; + ViewModelProvider.Factory viewModelFactory; private static final int SORTBYTICKET = 1; private static final int SORTBYNAME = 0; @@ -84,6 +84,8 @@ public class AttendeesFragment extends BaseFragment implemen private long currentPage = 1; private List attendeeList = new ArrayList<>(); + private AttendeesViewModel attendeesViewModel; + private RecyclerView.AdapterDataObserver observer; private static final String FILTER_SYNC = "FILTER_SYNC"; @@ -168,6 +170,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment binding = DataBindingUtil.inflate(inflater, R.layout.fragment_attendees, container, false); + attendeesViewModel = ViewModelProviders.of(this, viewModelFactory).get(AttendeesViewModel.class); binding.fabScanQr.getDrawable().setColorFilter(getResources().getColor(android.R.color.white), PorterDuff.Mode.SRC_ATOP); binding.fabScanQr.setOnClickListener(v -> scanningDecider.openScanQRActivity(getActivity(), eventId)); @@ -181,8 +184,13 @@ public void onStart() { setupSearchListener(); setupRefreshListener(); setupRecyclerView(); - getPresenter().attach(eventId, this); - getPresenter().start(); + attendeesViewModel.getProgress().observe(this, this::showProgress); + attendeesViewModel.getError().observe(this, this::showError); + attendeesViewModel.getAttendeesLiveData().observe(this, this::showResults); + attendeesViewModel.getShowScanButtonLiveData().observe(this, this::showScanButton); + attendeesViewModel.getUpdateAttendeeLiveData().observe(this, this::updateAttendee); + attendeesViewModel.loadAttendeesPageWise(FIRST_PAGE, false); + attendeesViewModel.listenChanges(); } @Override @@ -198,11 +206,6 @@ public void onStop() { searchView.setOnQueryTextListener(null); } - @Override - public Lazy getPresenterProvider() { - return presenterProvider; - } - private void setupSearchListener() { if (searchView == null) return; @@ -258,7 +261,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (!recyclerView.canScrollVertically(1)) { currentPage++; - getPresenter().loadAttendeesPageWise(currentPage, true); + attendeesViewModel.loadAttendeesPageWise(currentPage, true); } } @@ -271,7 +274,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } }); - SwipeController swipeController = new SwipeController(getPresenter(), attendeeList, context); + SwipeController swipeController = new SwipeController(attendeesViewModel, attendeeList, context); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController); itemTouchHelper.attachToRecyclerView(recyclerView); @@ -292,7 +295,7 @@ private void setupRefreshListener() { refreshLayout.setOnRefreshListener(() -> { refreshLayout.setRefreshing(false); attendeeList.clear(); - getPresenter().loadAttendeesPageWise(FIRST_PAGE, true); + attendeesViewModel.loadAttendeesPageWise(FIRST_PAGE, true); }); } @@ -300,7 +303,7 @@ private void setupRefreshListener() { public void onResume() { super.onResume(); attendeeList.clear(); - getPresenter().loadAttendeesPageWise(FIRST_PAGE, false); + attendeesViewModel.loadAttendeesPageWise(FIRST_PAGE, false); } // View Implementation diff --git a/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesPresenter.java b/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesPresenter.java deleted file mode 100644 index 0c2787642..000000000 --- a/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesPresenter.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.eventyay.organizer.core.attendee.list; - -import android.annotation.SuppressLint; -import androidx.annotation.VisibleForTesting; - -import com.raizlabs.android.dbflow.structure.BaseModel; - -import com.eventyay.organizer.common.mvp.presenter.AbstractDetailPresenter; -import com.eventyay.organizer.common.rx.Logger; -import com.eventyay.organizer.data.db.DatabaseChangeListener; -import com.eventyay.organizer.data.db.DbFlowDatabaseChangeListener; -import com.eventyay.organizer.data.attendee.Attendee; -import com.eventyay.organizer.data.attendee.AttendeeRepository; -import com.eventyay.organizer.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import io.reactivex.Observable; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -import static com.eventyay.organizer.common.rx.ViewTransformers.dispose; -import static com.eventyay.organizer.common.rx.ViewTransformers.emptiable; -import static com.eventyay.organizer.common.rx.ViewTransformers.erroneous; -import static com.eventyay.organizer.common.rx.ViewTransformers.progressiveErroneousRefresh; - -public class AttendeesPresenter extends AbstractDetailPresenter { - - private final AttendeeRepository attendeeRepository; - private final DatabaseChangeListener attendeeListener; - private final CompositeDisposable compositeDisposable = new CompositeDisposable(); - - private final List attendeeList = new ArrayList<>(); - - private static final long FIRST_PAGE = 1; - - @Inject - public AttendeesPresenter(AttendeeRepository attendeeRepository, DatabaseChangeListener attendeeListener) { - this.attendeeRepository = attendeeRepository; - this.attendeeListener = attendeeListener; - } - - @Override - public void start() { - loadAttendeesPageWise(FIRST_PAGE, false); - listenToModelChanges(); - } - - @Override - public void detach() { - super.detach(); - attendeeListener.stopListening(); - } - - public List getAttendees() { - return attendeeList; - } - - public void loadAttendees(boolean forceReload) { - if (getView() == null) - return; - - getView().showScanButton(false); - - getAttendeeSource(forceReload) - .compose(dispose(getDisposable())) - .compose(progressiveErroneousRefresh(getView(), forceReload)) - .toSortedList() - .compose(emptiable(getView(), attendeeList)) - .doFinally(() -> getView().showScanButton(!attendeeList.isEmpty())) - .subscribe(Logger::logSuccess, Logger::logError); - } - - public void loadAttendeesPageWise(long pageNumber, boolean forceReload) { - if (getView() == null) - return; - - getView().showScanButton(false); - - getAttendeeSourcePageWise(pageNumber, forceReload) - .compose(dispose(getDisposable())) - .compose(progressiveErroneousRefresh(getView(), forceReload)) - .toSortedList() - .compose(emptiable(getView(), attendeeList)) - .doFinally(() -> getView().showScanButton(!attendeeList.isEmpty())) - .subscribe(Logger::logSuccess, Logger::logError); - } - - private Observable getAttendeeSource(boolean forceReload) { - if (!forceReload && !getView().getAttendeeList().isEmpty() && isRotated()) - return Observable.fromIterable(getView().getAttendeeList()); - else - return attendeeRepository.getAttendees(getId(), forceReload); - } - - private Observable getAttendeeSourcePageWise(long pageNumber, boolean forceReload) { - if (!forceReload && !getView().getAttendeeList().isEmpty() && isRotated()) - return Observable.fromIterable(getView().getAttendeeList()); - else - return attendeeRepository.getAttendeesPageWise(getId(), pageNumber, forceReload); - } - - private void updateLocal(Attendee attendee) { - Utils.indexOf(getView().getAttendeeList(), attendee, (first, second) -> first.getId() == second.getId()) - .subscribeOn(Schedulers.computation()) - .subscribe(index -> getView().getAttendeeList().set(index, attendee), Logger::logError); - } - - private void listenToModelChanges() { - attendeeListener.startListening(); - - attendeeListener.getNotifier() - .compose(dispose(getDisposable())) - .compose(erroneous(getView())) - .filter(attendeeModelChange -> attendeeModelChange.getAction().equals(BaseModel.Action.UPDATE)) - .map(DbFlowDatabaseChangeListener.ModelChange::getModel) - .flatMap(filterAttendee -> attendeeRepository.getAttendee(filterAttendee.getId(), false)) - .subscribe(attendee -> { - getView().updateAttendee(attendee); - updateLocal(attendee); - }, Logger::logError); - } - - @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) - public AttendeesView getView() { - return super.getView(); - } - - @VisibleForTesting - public void setAttendeeList(List attendeeList) { - this.attendeeList.clear(); - this.attendeeList.addAll(attendeeList); - } - - @SuppressLint("CheckResult") - public void toggleCheckInState(List attendeeList, int swipedPosition) { - Attendee attendee = attendeeList.get(swipedPosition); - attendee.setChecking(true); - attendee.isCheckedIn = !attendee.isCheckedIn; - compositeDisposable.add( - attendeeRepository.scheduleToggle(attendee) - .subscribe(() -> { - // Nothing to do - }, Logger::logError)); - } -} - diff --git a/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesViewModel.java b/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesViewModel.java new file mode 100644 index 000000000..9cc770985 --- /dev/null +++ b/app/src/main/java/com/eventyay/organizer/core/attendee/list/AttendeesViewModel.java @@ -0,0 +1,163 @@ +package com.eventyay.organizer.core.attendee.list; + +import android.annotation.SuppressLint; + +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModel; + +import com.eventyay.organizer.common.ContextManager; +import com.eventyay.organizer.common.livedata.SingleEventLiveData; +import com.eventyay.organizer.common.rx.Logger; +import com.eventyay.organizer.data.attendee.Attendee; +import com.eventyay.organizer.data.attendee.AttendeeRepository; +import com.eventyay.organizer.data.db.DatabaseChangeListener; +import com.eventyay.organizer.data.db.DbFlowDatabaseChangeListener; +import com.eventyay.organizer.utils.ErrorUtils; +import com.eventyay.organizer.utils.Utils; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import io.reactivex.Observable; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; + +public class AttendeesViewModel extends ViewModel { + + private final AttendeeRepository attendeeRepository; + private final DatabaseChangeListener attendeeListener; + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + + private final List attendeeList = new ArrayList<>(); + + private final SingleEventLiveData progress = new SingleEventLiveData<>(); + private final SingleEventLiveData error = new SingleEventLiveData<>(); + private final SingleEventLiveData> attendeesLiveData = new SingleEventLiveData<>(); + private final SingleEventLiveData showScanButtonLiveData = new SingleEventLiveData<>(); + private final SingleEventLiveData updateAttendeeLiveData = new SingleEventLiveData<>(); + + private final long FIRST_PAGE = 1; + + private long eventId; + + @Inject + public AttendeesViewModel(AttendeeRepository attendeeRepository, DatabaseChangeListener attendeeListener) { + this.attendeeRepository = attendeeRepository; + this.attendeeListener = attendeeListener; + + eventId = ContextManager.getSelectedEvent().getId(); + } + + public LiveData getProgress() { + return progress; + } + + public LiveData getError() { + return error; + } + + public LiveData> getAttendeesLiveData() { + return attendeesLiveData; + } + + public LiveData getShowScanButtonLiveData() { + return showScanButtonLiveData; + } + + public LiveData getUpdateAttendeeLiveData() { + return updateAttendeeLiveData; + } + + public List getAttendees() { + return attendeeList; + } + + public void loadAttendees(boolean forceReload) { + + showScanButtonLiveData.setValue(false); + + compositeDisposable.add( + getAttendeeSource(forceReload) + .doOnSubscribe(disposable -> progress.setValue(true)) + .doFinally(() -> progress.setValue(false)) + .toSortedList() + .subscribe(attendees -> { + attendeeList.clear(); + attendeeList.addAll(attendees); + attendeesLiveData.setValue(attendees); + showScanButtonLiveData.setValue(!attendeeList.isEmpty()); + }, throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); + } + + public void loadAttendeesPageWise(long pageNumber, boolean forceReload) { + + showScanButtonLiveData.setValue(false); + + compositeDisposable.add( + getAttendeeSourcePageWise(pageNumber, forceReload) + .doOnSubscribe(disposable -> progress.setValue(true)) + .doFinally(() -> progress.setValue(false)) + .toSortedList() + .subscribe(attendees -> { + attendeeList.addAll(attendees); + attendeesLiveData.setValue(attendees); + showScanButtonLiveData.setValue(!attendeeList.isEmpty()); + }, throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); + } + + private Observable getAttendeeSource(boolean forceReload) { + if (!forceReload && !attendeeList.isEmpty()) + return Observable.fromIterable(attendeeList); + else + return attendeeRepository.getAttendees(eventId, forceReload); + } + + private Observable getAttendeeSourcePageWise(long pageNumber, boolean forceReload) { + if (!forceReload && !attendeeList.isEmpty()) + return Observable.fromIterable(attendeeList); + else + return attendeeRepository.getAttendeesPageWise(eventId, pageNumber, forceReload); + } + + private void updateLocal(Attendee attendee) { + Utils.indexOf(attendeeList, attendee, (first, second) -> first.getId() == second.getId()) + .subscribeOn(Schedulers.computation()) + .subscribe(index -> attendeeList.set(index, attendee), Logger::logError); + } + + public void listenChanges() { + attendeeListener.startListening(); + + attendeeListener.getNotifier() + .filter(attendeeModelChange -> attendeeModelChange.getAction().equals(BaseModel.Action.UPDATE)) + .map(DbFlowDatabaseChangeListener.ModelChange::getModel) + .flatMap(filterAttendee -> attendeeRepository.getAttendee(filterAttendee.getId(), false)) + .subscribe(attendee -> { + loadAttendeesPageWise(FIRST_PAGE, false); + updateAttendeeLiveData.setValue(attendee); + updateLocal(attendee); + }, Logger::logError); + } + + @VisibleForTesting + public void setAttendeeList(List attendeeList) { + this.attendeeList.clear(); + this.attendeeList.addAll(attendeeList); + } + + @SuppressLint("CheckResult") + public void toggleCheckInState(List attendeeList, int swipedPosition) { + Attendee attendee = attendeeList.get(swipedPosition); + attendee.setChecking(true); + attendee.isCheckedIn = !attendee.isCheckedIn; + compositeDisposable.add( + attendeeRepository.scheduleToggle(attendee) + .subscribe(() -> { + // Nothing to do + }, Logger::logError)); + } +} diff --git a/app/src/main/java/com/eventyay/organizer/core/attendee/list/SwipeController.java b/app/src/main/java/com/eventyay/organizer/core/attendee/list/SwipeController.java index 3176682c2..31f1ffb3f 100644 --- a/app/src/main/java/com/eventyay/organizer/core/attendee/list/SwipeController.java +++ b/app/src/main/java/com/eventyay/organizer/core/attendee/list/SwipeController.java @@ -17,16 +17,16 @@ public class SwipeController extends ItemTouchHelper.SimpleCallback { - private final AttendeesPresenter attendeesPresenter; + private final AttendeesViewModel attendeesViewModel; private List attendeeList; private final Paint paintGreen = new Paint(); private final Paint paintRed = new Paint(); private final Bitmap closeIcon; private final Bitmap doneIcon; - public SwipeController(AttendeesPresenter attendeesPresenter, List attendeeList, Context context) { + public SwipeController(AttendeesViewModel attendeesViewModel, List attendeeList, Context context) { super(0, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT); - this.attendeesPresenter = attendeesPresenter; + this.attendeesViewModel = attendeesViewModel; this.attendeeList = attendeeList; closeIcon = BitmapFactory.decodeResource(context.getResources(), R.drawable.close); @@ -56,7 +56,7 @@ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder v @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { int swipedPosition = viewHolder.getAdapterPosition(); - attendeesPresenter.toggleCheckInState(attendeeList, swipedPosition); + attendeesViewModel.toggleCheckInState(attendeeList, swipedPosition); } @Override diff --git a/app/src/test/java/com/eventyay/organizer/core/presenter/AttendeePresenterTest.java b/app/src/test/java/com/eventyay/organizer/core/presenter/AttendeePresenterTest.java deleted file mode 100644 index 49e49a5c1..000000000 --- a/app/src/test/java/com/eventyay/organizer/core/presenter/AttendeePresenterTest.java +++ /dev/null @@ -1,320 +0,0 @@ -package com.eventyay.organizer.core.presenter; - -import com.raizlabs.android.dbflow.structure.BaseModel; - -import com.eventyay.organizer.common.rx.Logger; -import com.eventyay.organizer.data.attendee.AttendeeRepository; -import com.eventyay.organizer.data.db.DbFlowDatabaseChangeListener; -import com.eventyay.organizer.data.db.DatabaseChangeListener; -import com.eventyay.organizer.data.attendee.Attendee; -import com.eventyay.organizer.core.attendee.list.AttendeesPresenter; -import com.eventyay.organizer.core.attendee.list.AttendeesView; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import io.reactivex.Observable; -import io.reactivex.android.plugins.RxAndroidPlugins; -import io.reactivex.plugins.RxJavaPlugins; -import io.reactivex.schedulers.Schedulers; -import io.reactivex.subjects.PublishSubject; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(JUnit4.class) -@SuppressWarnings("PMD.TooManyMethods") -public class AttendeePresenterTest { - - @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); - @Mock private AttendeesView attendeesView; - @Mock private AttendeeRepository attendeeRepository; - @Mock private DatabaseChangeListener changeListener; - - private static final long ID = 42; - private static final long PAGE = 1; - private AttendeesPresenter attendeesPresenter; - - private static final List ATTENDEES = Arrays.asList( - Attendee.builder().id(12).build(), - Attendee.builder().id(34).build(), - Attendee.builder().id(56).build(), - Attendee.builder().id(91).build(), - Attendee.builder().id(29).build(), - Attendee.builder().id(90).build(), - Attendee.builder().id(123).build() - ); - - static { - for (Attendee attendee : ATTENDEES) { - attendee.setFirstname("testFirstName" + attendee.getId()); - attendee.setLastname("testLastName" + attendee.getId()); - attendee.setEmail("testEmail" + attendee.getId() + "@test.com"); - } - } - - @Before - public void setUp() { - attendeesPresenter = new AttendeesPresenter(attendeeRepository, changeListener); - attendeesPresenter.attach(ID, attendeesView); - - RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); - RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline()); - RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); - } - - @After - public void tearDown() { - RxJavaPlugins.reset(); - RxAndroidPlugins.reset(); - } - - @Test - public void shouldLoadAttendeesAutomatically() { - when(attendeeRepository.getAttendeesPageWise(ID, PAGE, false)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - when(changeListener.getNotifier()).thenReturn(PublishSubject.create()); - - attendeesPresenter.start(); - - verify(attendeeRepository).getAttendeesPageWise(ID, PAGE, false); - } - - @Test - public void shouldDetachViewOnStop() { - when(attendeeRepository.getAttendeesPageWise(ID, PAGE, false)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - when(changeListener.getNotifier()).thenReturn(PublishSubject.create()); - - attendeesPresenter.start(); - - assertNotNull(attendeesPresenter.getView()); - - attendeesPresenter.detach(); - - assertTrue(attendeesPresenter.getDisposable().isDisposed()); - } - - @Test - public void shouldShowAttendeeError() { - when(attendeeRepository.getAttendees(ID, false)) - .thenReturn(TestUtil.ERROR_OBSERVABLE); - - InOrder inOrder = Mockito.inOrder(attendeeRepository, attendeesView); - - attendeesPresenter.loadAttendees(false); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeeRepository).getAttendees(ID, false); - inOrder.verify(attendeesView).showProgress(true); - inOrder.verify(attendeesView).showError(Logger.TEST_MESSAGE); - inOrder.verify(attendeesView).showProgress(false); - } - - @Test - public void shouldLoadAttendeesSuccessfully() { - when(attendeeRepository.getAttendees(ID, false)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - - InOrder inOrder = Mockito.inOrder(attendeeRepository, attendeesView); - - attendeesPresenter.loadAttendees(false); - - // TODO: Fix flaky test for ATTENDEES - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeeRepository).getAttendees(ID, false); - inOrder.verify(attendeesView).showProgress(true); - inOrder.verify(attendeesView).showResults(any()); - inOrder.verify(attendeesView).showScanButton(true); - inOrder.verify(attendeesView).showProgress(false); - } - - @Test - public void shouldRefreshAttendeesSuccessfully() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - - InOrder inOrder = Mockito.inOrder(attendeeRepository, attendeesView); - - attendeesPresenter.loadAttendees(true); - - // TODO: Fix flaky test for ATTENDEES - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeeRepository).getAttendees(ID, true); - inOrder.verify(attendeesView).showProgress(true); - inOrder.verify(attendeesView).onRefreshComplete(true); - inOrder.verify(attendeesView).showResults(any()); - inOrder.verify(attendeesView).showScanButton(true); - inOrder.verify(attendeesView).showProgress(false); - } - - @Test - public void shouldShowEmptyViewOnNoItemAfterSwipeRefresh() { - ArrayList attendees = new ArrayList<>(); - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.fromIterable(attendees)); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showEmptyView(false); - inOrder.verify(attendeesView).showResults(attendees); - inOrder.verify(attendeesView).showEmptyView(true); - } - - @Test - public void shouldNotShowScanButtonOnNoItemAfterSwipeRefresh() { - ArrayList attendees = new ArrayList<>(); - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.fromIterable(attendees)); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeesView).onRefreshComplete(true); - inOrder.verify(attendeesView).showScanButton(false); - } - - @Test - public void shouldShowEmptyViewOnSwipeRefreshError() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(TestUtil.ERROR_OBSERVABLE); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showEmptyView(false); - inOrder.verify(attendeesView).showError(Logger.TEST_MESSAGE); - inOrder.verify(attendeesView).showEmptyView(true); - } - - @Test - public void shouldNotShowScanButtonOnSwipeRefreshError() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(TestUtil.ERROR_OBSERVABLE); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeesView).onRefreshComplete(false); - inOrder.verify(attendeesView).showScanButton(false); - } - - @Test - public void shouldNotShowEmptyViewIfNonEmptyAttendeeListOnSwipeRefreshError() { - attendeesPresenter.setAttendeeList(ATTENDEES); - - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(TestUtil.ERROR_OBSERVABLE); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showEmptyView(false); - inOrder.verify(attendeesView).showError(Logger.TEST_MESSAGE); - inOrder.verify(attendeesView).showEmptyView(false); - } - - @Test - public void shouldShowScanButtonIfNonEmptyAttendeeListOnSwipeRefreshError() { - attendeesPresenter.setAttendeeList(ATTENDEES); - - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.error(Logger.TEST_ERROR)); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeesView).onRefreshComplete(false); - inOrder.verify(attendeesView).showScanButton(true); - } - - @Test - public void shouldNotShowEmptyViewOnSwipeRefreshSuccess() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - // TODO: Fix flaky test for ATTENDEES - - inOrder.verify(attendeesView).showEmptyView(false); - inOrder.verify(attendeesView).showResults(any()); - inOrder.verify(attendeesView).showEmptyView(false); - } - - @Test - public void shouldShowScanButtonOnSwipeRefreshSuccess() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(Observable.fromIterable(ATTENDEES)); - - InOrder inOrder = Mockito.inOrder(attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeesView).onRefreshComplete(true); - inOrder.verify(attendeesView).showScanButton(true); - } - - @Test - public void shouldRefreshAttendeesOnError() { - when(attendeeRepository.getAttendees(ID, true)) - .thenReturn(TestUtil.ERROR_OBSERVABLE); - - InOrder inOrder = Mockito.inOrder(attendeeRepository, attendeesView); - - attendeesPresenter.loadAttendees(true); - - inOrder.verify(attendeesView).showScanButton(false); - inOrder.verify(attendeeRepository).getAttendees(ID, true); - inOrder.verify(attendeesView).showProgress(true); - inOrder.verify(attendeesView).showError(anyString()); - inOrder.verify(attendeesView).onRefreshComplete(false); - inOrder.verify(attendeesView).showProgress(false); - } - - @Test - public void shouldToggleAttendeesSuccessfully() { - PublishSubject> publishSubject = PublishSubject.create(); - - when(attendeeRepository.getAttendeesPageWise(ID, PAGE, false)).thenReturn(Observable.fromIterable(ATTENDEES)); - when(attendeeRepository.getAttendee(ATTENDEES.get(2).getId(), false)).thenReturn(Observable.just(ATTENDEES.get(2))); - when(changeListener.getNotifier()).thenReturn(publishSubject); - - attendeesPresenter.start(); - publishSubject.onNext(new DbFlowDatabaseChangeListener.ModelChange<>(ATTENDEES.get(2), BaseModel.Action.UPDATE)); - - verify(attendeesView).updateAttendee(ATTENDEES.get(2)); - } - -} From bca1a60013cc7b0a57cdeb919904cec9413e8234 Mon Sep 17 00:00:00 2001 From: Shridhar Goel Date: Fri, 21 Jun 2019 03:07:49 +0530 Subject: [PATCH 05/36] chore: Set event start time as default ticket sale end time (#1729) --- .../core/ticket/create/CreateTicketViewModel.java | 11 +++++------ .../core/ticket/create/TicketCreateViewModelTest.java | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/eventyay/organizer/core/ticket/create/CreateTicketViewModel.java b/app/src/main/java/com/eventyay/organizer/core/ticket/create/CreateTicketViewModel.java index 4cf57b1f2..fb96dc92f 100644 --- a/app/src/main/java/com/eventyay/organizer/core/ticket/create/CreateTicketViewModel.java +++ b/app/src/main/java/com/eventyay/organizer/core/ticket/create/CreateTicketViewModel.java @@ -2,6 +2,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModel; + import com.eventyay.organizer.common.ContextManager; import com.eventyay.organizer.common.livedata.SingleEventLiveData; import com.eventyay.organizer.data.event.Event; @@ -9,6 +10,7 @@ import com.eventyay.organizer.data.ticket.TicketRepository; import com.eventyay.organizer.utils.DateUtils; import com.eventyay.organizer.utils.ErrorUtils; + import org.threeten.bp.LocalDateTime; import org.threeten.bp.ZonedDateTime; import org.threeten.bp.format.DateTimeParseException; @@ -34,12 +36,9 @@ public CreateTicketViewModel(TicketRepository ticketRepository) { String startDate = DateUtils.formatDateToIso(current); ticket.setSalesStartsAt(startDate); - LocalDateTime salesEndTime = current.plusDays(10); - LocalDateTime eventEndTime = DateUtils.getIsoOffsetTimeFromTimestamp(ContextManager.getSelectedEvent().getEndsAt()); - //if less than 10 days are available in the event. - if (salesEndTime.isAfter(eventEndTime) && !eventEndTime.isBefore(current)) { - salesEndTime = eventEndTime; - } + LocalDateTime salesEndTime = DateUtils.getIsoOffsetTimeFromTimestamp( + ContextManager.getSelectedEvent().getStartsAt()); + String endDate = DateUtils.formatDateToIso(salesEndTime); ticket.setSalesEndsAt(endDate); ticket.setType("free"); diff --git a/app/src/test/java/com/eventyay/organizer/core/ticket/create/TicketCreateViewModelTest.java b/app/src/test/java/com/eventyay/organizer/core/ticket/create/TicketCreateViewModelTest.java index 8c9beaead..fcaaff30d 100644 --- a/app/src/test/java/com/eventyay/organizer/core/ticket/create/TicketCreateViewModelTest.java +++ b/app/src/test/java/com/eventyay/organizer/core/ticket/create/TicketCreateViewModelTest.java @@ -70,7 +70,8 @@ public void setUp() { private void setupMockEvent() { when(event.getTimezone()).thenReturn("UTC"); - when(event.getEndsAt()).thenReturn("2018-12-14T23:59:59.123456+00:00"); + when(event.getStartsAt()).thenReturn("2019-06-18T23:59:59.123456+00:00"); + when(event.getEndsAt()).thenReturn("2019-06-20T23:59:59.123456+00:00"); } @After From 468df3811f4aa384fa769a9d9a85baa9fe86da9e Mon Sep 17 00:00:00 2001 From: Shridhar Goel Date: Sun, 23 Jun 2019 02:33:22 +0530 Subject: [PATCH 06/36] chore: Migrate CreateSessionPresenter to ViewModel (#1738) --- .../common/di/module/ViewModelModule.java | 6 + .../session/create/CreateSessionFragment.java | 32 +-- .../create/CreateSessionPresenter.java | 126 ---------- .../create/CreateSessionViewModel.java | 156 ++++++++++++ .../presenter/CreateSessionPresenterTest.java | 193 --------------- .../create/CreateSessionViewModelTest.java | 231 ++++++++++++++++++ 6 files changed, 411 insertions(+), 333 deletions(-) delete mode 100644 app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionPresenter.java create mode 100644 app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionViewModel.java delete mode 100644 app/src/test/java/com/eventyay/organizer/core/presenter/CreateSessionPresenterTest.java create mode 100644 app/src/test/java/com/eventyay/organizer/core/session/create/CreateSessionViewModelTest.java diff --git a/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java b/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java index 941565a68..db2438311 100644 --- a/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java +++ b/app/src/main/java/com/eventyay/organizer/common/di/module/ViewModelModule.java @@ -33,6 +33,7 @@ import com.eventyay.organizer.core.organizer.update.UpdateOrganizerInfoViewModel; import com.eventyay.organizer.core.role.list.RoleListViewModel; import com.eventyay.organizer.core.role.invite.RoleInviteViewModel; +import com.eventyay.organizer.core.session.create.CreateSessionViewModel; import com.eventyay.organizer.core.settings.autocheckin.AutoCheckInViewModel; import com.eventyay.organizer.core.settings.restriction.TicketSettingsViewModel; import com.eventyay.organizer.core.share.ShareEventViewModel; @@ -246,6 +247,11 @@ public abstract class ViewModelModule { @ViewModelKey(AttendeesViewModel.class) public abstract ViewModel bindAttendeesViewModel(AttendeesViewModel attendeesViewModel); + @Binds + @IntoMap + @ViewModelKey(CreateSessionViewModel.class) + public abstract ViewModel bindCreateSessionViewModel(CreateSessionViewModel createSessionViewModel); + @Binds public abstract ViewModelProvider.Factory bindViewModelFactory(OrgaViewModelFactory factory); diff --git a/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionFragment.java b/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionFragment.java index 1d2dba362..8c3bc47c0 100644 --- a/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionFragment.java +++ b/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionFragment.java @@ -3,6 +3,9 @@ import androidx.databinding.DataBindingUtil; import android.os.Bundle; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; +import androidx.lifecycle.ViewModelProviders; + import com.google.android.material.textfield.TextInputLayout; import android.text.Editable; import android.text.TextUtils; @@ -25,14 +28,13 @@ import javax.inject.Inject; import br.com.ilhasoft.support.validation.Validator; -import dagger.Lazy; import static com.eventyay.organizer.ui.ViewUtils.showView; -public class CreateSessionFragment extends BaseFragment implements CreateSessionView { +public class CreateSessionFragment extends BaseFragment implements CreateSessionView { @Inject - Lazy presenterProvider; + ViewModelProvider.Factory viewModelFactory; private SessionCreateLayoutBinding binding; private Validator validator; @@ -40,6 +42,8 @@ public class CreateSessionFragment extends BaseFragment private static final String SESSION_KEY = "session_id"; private ArrayAdapter sessionStateAdapter; + private CreateSessionViewModel createSessionViewModel; + private boolean isSessionUpdating; private long trackId; private long eventId; @@ -84,14 +88,15 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate(inflater, R.layout.session_create_layout, container, false); + createSessionViewModel = ViewModelProviders.of(this, viewModelFactory).get(CreateSessionViewModel.class); validator = new Validator(binding.form); binding.submit.setOnClickListener(view -> { if (validator.validate()) { if (isSessionUpdating) { - getPresenter().updateSession(trackId, eventId); + createSessionViewModel.updateSession(trackId, eventId); } else { - getPresenter().createSession(trackId, eventId); + createSessionViewModel.createSession(trackId, eventId); } } }); @@ -110,11 +115,15 @@ private void setUpSpinner() { @Override public void onStart() { super.onStart(); - getPresenter().attach(this); - binding.setSession(getPresenter().getSession()); + createSessionViewModel.getProgress().observe(this, this::showProgress); + createSessionViewModel.getDismiss().observe(this, (dismiss) -> dismiss()); + createSessionViewModel.getSuccess().observe(this, this::onSuccess); + createSessionViewModel.getError().observe(this, this::showError); + createSessionViewModel.getSessionLiveData().observe(this, this::setSession); + binding.setSession(createSessionViewModel.getSession()); if (isSessionUpdating) { - getPresenter().loadSession(sessionId); + createSessionViewModel.loadSession(sessionId); } validate(binding.form.slidesUrlLayout, ValidateUtils::validateUrl, getResources().getString(R.string.url_validation_error)); @@ -157,7 +166,7 @@ public void afterTextChanged(Editable editable) { @Override public void setSession(Session session) { binding.setSession(session); - String state = getPresenter().getSession().getState(); + String state = createSessionViewModel.getSession().getState(); int statePosition = sessionStateAdapter.getPosition(state); binding.form.spinner.setSelection(statePosition); } @@ -171,11 +180,6 @@ protected int getTitle() { } } - @Override - public Lazy getPresenterProvider() { - return presenterProvider; - } - @Override public void showProgress(boolean show) { showView(binding.progressBar, show); diff --git a/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionPresenter.java b/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionPresenter.java deleted file mode 100644 index c82476bc5..000000000 --- a/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionPresenter.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.eventyay.organizer.core.session.create; - -import com.eventyay.organizer.common.mvp.presenter.AbstractBasePresenter; -import com.eventyay.organizer.common.rx.Logger; -import com.eventyay.organizer.data.event.Event; -import com.eventyay.organizer.data.session.Session; -import com.eventyay.organizer.data.session.SessionRepository; -import com.eventyay.organizer.data.tracks.Track; -import com.eventyay.organizer.utils.DateUtils; -import com.eventyay.organizer.utils.StringUtils; -import org.threeten.bp.LocalDateTime; -import org.threeten.bp.ZonedDateTime; -import org.threeten.bp.format.DateTimeParseException; - -import javax.inject.Inject; - -import static com.eventyay.organizer.common.rx.ViewTransformers.dispose; -import static com.eventyay.organizer.common.rx.ViewTransformers.progressiveErroneous; - -public class CreateSessionPresenter extends AbstractBasePresenter { - - private final SessionRepository sessionRepository; - private Session session = new Session(); - - @Inject - public CreateSessionPresenter(SessionRepository sessionRepository) { - this.sessionRepository = sessionRepository; - - LocalDateTime current = LocalDateTime.now(); - - String isoDate = DateUtils.formatDateToIso(current); - session.setStartsAt(isoDate); - session.setEndsAt(isoDate); - } - - @Override - public void start() { - // Nothing to do - } - - public Session getSession() { - return session; - } - - private boolean verify() { - try { - ZonedDateTime start = DateUtils.getDate(session.getStartsAt()); - ZonedDateTime end = DateUtils.getDate(session.getEndsAt()); - - if (!end.isAfter(start)) { - getView().showError("End time should be after start time"); - return false; - } - return true; - } catch (DateTimeParseException pe) { - getView().showError("Please enter date in correct format"); - return false; - } - } - - protected void nullifyEmptyFields(Session session) { - session.setSlidesUrl(StringUtils.emptyToNull(session.getSlidesUrl())); - session.setAudioUrl(StringUtils.emptyToNull(session.getAudioUrl())); - session.setVideoUrl(StringUtils.emptyToNull(session.getVideoUrl())); - session.setSignupUrl(StringUtils.emptyToNull(session.getSignupUrl())); - } - - //Used for loading the session information on start - public void loadSession(long sessionId) { - sessionRepository - .getSession(sessionId, false) - .compose(dispose(getDisposable())) - .compose(progressiveErroneous(getView())) - .doFinally(this::showSession) - .subscribe(loadedSession -> this.session = (Session) loadedSession, Logger::logError); - } - - private void showSession() { - getView().setSession(session); - } - - //method called for updating an session - public void updateSession(long trackId, long eventId) { - Track track = new Track(); - Event event = new Event(); - - track.setId(trackId); - event.setId(eventId); - session.setTrack(track); - session.setEvent(event); - nullifyEmptyFields(session); - - sessionRepository - .updateSession(session) - .compose(dispose(getDisposable())) - .compose(progressiveErroneous(getView())) - .subscribe(updatedSession -> { - getView().onSuccess("Session Updated Successfully"); - getView().dismiss(); - }, Logger::logError); - } - - public void createSession(long trackId, long eventId) { - if (!verify()) - return; - - Track track = new Track(); - Event event = new Event(); - - track.setId(trackId); - event.setId(eventId); - session.setTrack(track); - session.setEvent(event); - - nullifyEmptyFields(session); - - sessionRepository - .createSession(session) - .compose(dispose(getDisposable())) - .compose(progressiveErroneous(getView())) - .subscribe(createdSession -> { - getView().onSuccess("Session Created"); - getView().dismiss(); - }, Logger::logError); - } -} diff --git a/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionViewModel.java b/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionViewModel.java new file mode 100644 index 000000000..8825de632 --- /dev/null +++ b/app/src/main/java/com/eventyay/organizer/core/session/create/CreateSessionViewModel.java @@ -0,0 +1,156 @@ +package com.eventyay.organizer.core.session.create; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModel; + +import com.eventyay.organizer.common.livedata.SingleEventLiveData; +import com.eventyay.organizer.data.event.Event; +import com.eventyay.organizer.data.session.Session; +import com.eventyay.organizer.data.session.SessionRepository; +import com.eventyay.organizer.data.tracks.Track; +import com.eventyay.organizer.utils.DateUtils; +import com.eventyay.organizer.utils.ErrorUtils; +import com.eventyay.organizer.utils.StringUtils; + +import org.threeten.bp.LocalDateTime; +import org.threeten.bp.ZonedDateTime; +import org.threeten.bp.format.DateTimeParseException; + +import javax.inject.Inject; + +import io.reactivex.disposables.CompositeDisposable; + +public class CreateSessionViewModel extends ViewModel { + + private final SessionRepository sessionRepository; + private Session session = new Session(); + + private final CompositeDisposable compositeDisposable = new CompositeDisposable(); + private final SingleEventLiveData progress = new SingleEventLiveData<>(); + private final SingleEventLiveData error = new SingleEventLiveData<>(); + private final SingleEventLiveData success = new SingleEventLiveData<>(); + private final SingleEventLiveData dismiss = new SingleEventLiveData<>(); + private final SingleEventLiveData sessionLiveData = new SingleEventLiveData<>(); + + @Inject + public CreateSessionViewModel(SessionRepository sessionRepository) { + this.sessionRepository = sessionRepository; + + LocalDateTime current = LocalDateTime.now(); + + String isoDate = DateUtils.formatDateToIso(current); + session.setStartsAt(isoDate); + session.setEndsAt(isoDate); + } + + public LiveData getProgress() { + return progress; + } + + public LiveData getSuccess() { + return success; + } + + public LiveData getDismiss() { + return dismiss; + } + + public LiveData getError() { + return error; + } + + public LiveData getSessionLiveData() { + return sessionLiveData; + } + + public Session getSession() { + return session; + } + + private boolean verify() { + try { + ZonedDateTime start = DateUtils.getDate(session.getStartsAt()); + ZonedDateTime end = DateUtils.getDate(session.getEndsAt()); + + if (!end.isAfter(start)) { + error.setValue("End time should be after start time"); + return false; + } + return true; + } catch (DateTimeParseException pe) { + error.setValue("Please enter date in correct format"); + return false; + } + } + + protected void nullifyEmptyFields(Session session) { + session.setSlidesUrl(StringUtils.emptyToNull(session.getSlidesUrl())); + session.setAudioUrl(StringUtils.emptyToNull(session.getAudioUrl())); + session.setVideoUrl(StringUtils.emptyToNull(session.getVideoUrl())); + session.setSignupUrl(StringUtils.emptyToNull(session.getSignupUrl())); + } + + // Used for loading the session information on start + public void loadSession(long sessionId) { + + compositeDisposable.add( + sessionRepository + .getSession(sessionId, false) + .doOnSubscribe(disposable -> progress.setValue(true)) + .doFinally(() -> progress.setValue(false)) + .doFinally(this::showSession) + .subscribe(loadedSession -> this.session = loadedSession, + throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); + } + + private void showSession() { + sessionLiveData.setValue(session); + } + + // Method called for updating an session + public void updateSession(long trackId, long eventId) { + Track track = new Track(); + Event event = new Event(); + + track.setId(trackId); + event.setId(eventId); + session.setTrack(track); + session.setEvent(event); + nullifyEmptyFields(session); + + compositeDisposable.add( + sessionRepository + .updateSession(session) + .doOnSubscribe(disposable -> progress.setValue(true)) + .doFinally(() -> progress.setValue(false)) + .subscribe(updatedSession -> { + success.setValue("Session Updated Successfully"); + dismiss.call(); + }, throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); + } + + public void createSession(long trackId, long eventId) { + if (!verify()) + return; + + Track track = new Track(); + Event event = new Event(); + + track.setId(trackId); + event.setId(eventId); + session.setTrack(track); + session.setEvent(event); + + nullifyEmptyFields(session); + + compositeDisposable.add( + sessionRepository + .createSession(session) + .doOnSubscribe(disposable -> progress.setValue(true)) + .doFinally(() -> progress.setValue(false)) + .subscribe(createdSession -> { + success.setValue("Session Created"); + dismiss.call(); + }, throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); + } +} diff --git a/app/src/test/java/com/eventyay/organizer/core/presenter/CreateSessionPresenterTest.java b/app/src/test/java/com/eventyay/organizer/core/presenter/CreateSessionPresenterTest.java deleted file mode 100644 index fd1e27464..000000000 --- a/app/src/test/java/com/eventyay/organizer/core/presenter/CreateSessionPresenterTest.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.eventyay.organizer.core.presenter; - -import com.eventyay.organizer.core.session.create.CreateSessionPresenter; -import com.eventyay.organizer.core.session.create.CreateSessionView; -import com.eventyay.organizer.data.session.Session; -import com.eventyay.organizer.data.session.SessionRepository; -import com.eventyay.organizer.utils.DateUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.threeten.bp.LocalDateTime; - -import io.reactivex.Observable; -import io.reactivex.android.plugins.RxAndroidPlugins; -import io.reactivex.plugins.RxJavaPlugins; -import io.reactivex.schedulers.Schedulers; - -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class CreateSessionPresenterTest { - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - @Mock - private CreateSessionView createSessionView; - @Mock - private SessionRepository sessionRepository; - - private CreateSessionPresenter createSessionPresenter; - private static final Session SESSION = Session.builder().id(2L).title("dd").build(); - private static final String ERROR = "Error"; - private static final long EVENT_ID = 5L; - private static final long TRACK_ID = 5L; - - @Before - public void setUp() { - RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); - RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); - - createSessionPresenter = new CreateSessionPresenter(sessionRepository); - createSessionPresenter.attach(createSessionView); - } - - @After - public void tearDown() { - RxJavaPlugins.reset(); - RxAndroidPlugins.reset(); - } - - @Test - public void shouldRejectWrongDates() { - Session session = createSessionPresenter.getSession(); - - String isoDate = DateUtils.formatDateToIso(LocalDateTime.now()); - session.setStartsAt(isoDate); - session.setEndsAt(isoDate); - - createSessionPresenter.createSession(TRACK_ID, EVENT_ID); - - verify(createSessionView).showError(anyString()); - verify(sessionRepository, never()).createSession(any()); - } - - @Test - public void shouldAcceptCorrectDates() { - Session session = createSessionPresenter.getSession(); - - when(sessionRepository.createSession(session)).thenReturn(Observable.empty()); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateThen); - - createSessionPresenter.createSession(TRACK_ID, EVENT_ID); - - verify(createSessionView, never()).showError(anyString()); - verify(sessionRepository).createSession(session); - } - - @Test - public void shouldNullifyEmptyFields() { - Session session = createSessionPresenter.getSession(); - when(sessionRepository.createSession(session)).thenReturn(Observable.just(session)); - - session.setSlidesUrl(""); - session.setAudioUrl(""); - session.setVideoUrl(""); - session.setSignupUrl(""); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateMax = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateMax); - - createSessionPresenter.createSession(TRACK_ID, EVENT_ID); - assertNull(session.getSlidesUrl()); - assertNull(session.getAudioUrl()); - assertNull(session.getVideoUrl()); - assertNull(session.getSignupUrl()); - } - - @Test - public void shouldShowSuccessOnCreated() { - Session session = createSessionPresenter.getSession(); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateThen); - - when(sessionRepository.createSession(createSessionPresenter.getSession())).thenReturn(Observable.just(SESSION)); - - createSessionPresenter.createSession(TRACK_ID, EVENT_ID); - - InOrder inOrder = Mockito.inOrder(createSessionView); - - inOrder.verify(createSessionView).showProgress(true); - inOrder.verify(createSessionView).onSuccess(anyString()); - inOrder.verify(createSessionView).showProgress(false); - } - - @Test - public void shouldShowErrorOnFailure() { - Session session = createSessionPresenter.getSession(); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateThen); - - when(sessionRepository.createSession(createSessionPresenter.getSession())).thenReturn(Observable.error(new Throwable(ERROR))); - - createSessionPresenter.createSession(TRACK_ID, EVENT_ID); - - InOrder inOrder = Mockito.inOrder(createSessionView); - - inOrder.verify(createSessionView).showProgress(true); - inOrder.verify(createSessionView).showError(ERROR); - inOrder.verify(createSessionView).showProgress(false); - } - - @Test - public void shouldShowSuccessOnUpdated() { - Session session = createSessionPresenter.getSession(); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateThen); - - when(sessionRepository.updateSession(createSessionPresenter.getSession())).thenReturn(Observable.just(SESSION)); - - createSessionPresenter.updateSession(TRACK_ID, EVENT_ID); - - InOrder inOrder = Mockito.inOrder(createSessionView); - - inOrder.verify(createSessionView).showProgress(true); - inOrder.verify(createSessionView).onSuccess(anyString()); - inOrder.verify(createSessionView).showProgress(false); - } - - @Test - public void shouldShowErrorOnUpdateFailure() { - Session session = createSessionPresenter.getSession(); - - String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); - String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); - session.setStartsAt(isoDateNow); - session.setEndsAt(isoDateThen); - - when(sessionRepository.updateSession(createSessionPresenter.getSession())).thenReturn(Observable.error(new Throwable(ERROR))); - - createSessionPresenter.updateSession(TRACK_ID, EVENT_ID); - - InOrder inOrder = Mockito.inOrder(createSessionView); - - inOrder.verify(createSessionView).showProgress(true); - inOrder.verify(createSessionView).showError(ERROR); - inOrder.verify(createSessionView).showProgress(false); - } -} diff --git a/app/src/test/java/com/eventyay/organizer/core/session/create/CreateSessionViewModelTest.java b/app/src/test/java/com/eventyay/organizer/core/session/create/CreateSessionViewModelTest.java new file mode 100644 index 000000000..e0a05b56f --- /dev/null +++ b/app/src/test/java/com/eventyay/organizer/core/session/create/CreateSessionViewModelTest.java @@ -0,0 +1,231 @@ +package com.eventyay.organizer.core.session.create; + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule; +import androidx.lifecycle.Observer; + +import com.eventyay.organizer.data.session.Session; +import com.eventyay.organizer.data.session.SessionRepository; +import com.eventyay.organizer.utils.DateUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.threeten.bp.LocalDateTime; + +import io.reactivex.Observable; +import io.reactivex.android.plugins.RxAndroidPlugins; +import io.reactivex.plugins.RxJavaPlugins; +import io.reactivex.schedulers.Schedulers; + +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; + +@RunWith(JUnit4.class) +public class CreateSessionViewModelTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Rule + public TestRule rule = new InstantTaskExecutorRule(); + + @Mock + private SessionRepository sessionRepository; + + @Mock + Observer error; + @Mock + Observer progress; + @Mock + Observer success; + @Mock + Observer dismiss; + + private CreateSessionViewModel createSessionViewModel; + private static final Session SESSION = Session.builder().id(2L).title("dd").build(); + private static final String ERROR = "Error"; + private static final long EVENT_ID = 5L; + private static final long TRACK_ID = 5L; + + @Before + public void setUp() { + RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline()); + RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); + + createSessionViewModel = new CreateSessionViewModel(sessionRepository); + } + + @After + public void tearDown() { + RxJavaPlugins.reset(); + RxAndroidPlugins.reset(); + } + + @Test + public void shouldRejectWrongDates() { + Session session = createSessionViewModel.getSession(); + + String isoDate = DateUtils.formatDateToIso(LocalDateTime.now()); + session.setStartsAt(isoDate); + session.setEndsAt(isoDate); + + InOrder inOrder = Mockito.inOrder(sessionRepository, error); + + createSessionViewModel.getError().observeForever(error); + + createSessionViewModel.createSession(TRACK_ID, EVENT_ID); + + inOrder.verify(error).onChanged(anyString()); + inOrder.verify(sessionRepository, never()).createSession(any()); + } + + @Test + public void shouldAcceptCorrectDates() { + Session session = createSessionViewModel.getSession(); + + when(sessionRepository.createSession(session)).thenReturn(Observable.empty()); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateThen); + + InOrder inOrder = Mockito.inOrder(sessionRepository, error); + + createSessionViewModel.getError().observeForever(error); + + createSessionViewModel.createSession(TRACK_ID, EVENT_ID); + + inOrder.verify(error, never()).onChanged(anyString()); + inOrder.verify(sessionRepository).createSession(session); + } + + @Test + public void shouldNullifyEmptyFields() { + Session session = createSessionViewModel.getSession(); + when(sessionRepository.createSession(session)).thenReturn(Observable.just(session)); + + session.setSlidesUrl(""); + session.setAudioUrl(""); + session.setVideoUrl(""); + session.setSignupUrl(""); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateMax = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateMax); + + createSessionViewModel.createSession(TRACK_ID, EVENT_ID); + assertNull(session.getSlidesUrl()); + assertNull(session.getAudioUrl()); + assertNull(session.getVideoUrl()); + assertNull(session.getSignupUrl()); + } + + @Test + public void shouldShowSuccessOnCreated() { + Session session = createSessionViewModel.getSession(); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateThen); + + when(sessionRepository.createSession(createSessionViewModel.getSession())).thenReturn(Observable.just(SESSION)); + + InOrder inOrder = Mockito.inOrder(progress, success, dismiss); + + createSessionViewModel.getProgress().observeForever(progress); + createSessionViewModel.getSuccess().observeForever(success); + createSessionViewModel.getDismiss().observeForever(dismiss); + + createSessionViewModel.createSession(TRACK_ID, EVENT_ID); + + inOrder.verify(progress).onChanged(true); + inOrder.verify(success).onChanged(anyString()); + inOrder.verify(dismiss).onChanged(null); + inOrder.verify(progress).onChanged(false); + } + + @Test + public void shouldShowErrorOnFailure() { + Session session = createSessionViewModel.getSession(); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateThen); + + when(sessionRepository.createSession(createSessionViewModel.getSession())).thenReturn(Observable.error(new Throwable(ERROR))); + + InOrder inOrder = Mockito.inOrder(progress, error); + + createSessionViewModel.getProgress().observeForever(progress); + createSessionViewModel.getError().observeForever(error); + + createSessionViewModel.createSession(TRACK_ID, EVENT_ID); + + inOrder.verify(progress).onChanged(true); + inOrder.verify(error).onChanged(anyString()); + inOrder.verify(progress).onChanged(false); + } + + @Test + public void shouldShowSuccessOnUpdated() { + Session session = createSessionViewModel.getSession(); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateThen); + + when(sessionRepository.updateSession(createSessionViewModel.getSession())).thenReturn(Observable.just(SESSION)); + + InOrder inOrder = Mockito.inOrder(progress, success, dismiss); + + createSessionViewModel.getProgress().observeForever(progress); + createSessionViewModel.getSuccess().observeForever(success); + createSessionViewModel.getDismiss().observeForever(dismiss); + + createSessionViewModel.updateSession(TRACK_ID, EVENT_ID); + + inOrder.verify(progress).onChanged(true); + inOrder.verify(success).onChanged(anyString()); + inOrder.verify(dismiss).onChanged(null); + inOrder.verify(progress).onChanged(false); + } + + @Test + public void shouldShowErrorOnUpdateFailure() { + Session session = createSessionViewModel.getSession(); + + String isoDateNow = DateUtils.formatDateToIso(LocalDateTime.now()); + String isoDateThen = DateUtils.formatDateToIso(LocalDateTime.MAX); + session.setStartsAt(isoDateNow); + session.setEndsAt(isoDateThen); + + when(sessionRepository.updateSession(createSessionViewModel.getSession())).thenReturn(Observable.error(new Throwable(ERROR))); + + InOrder inOrder = Mockito.inOrder(progress, error); + + createSessionViewModel.getProgress().observeForever(progress); + createSessionViewModel.getError().observeForever(error); + + createSessionViewModel.updateSession(TRACK_ID, EVENT_ID); + + inOrder.verify(progress).onChanged(true); + inOrder.verify(error).onChanged(anyString()); + inOrder.verify(progress).onChanged(false); + } +} From caf4dbf6fa3512fec8364a3b6369b4823708ab09 Mon Sep 17 00:00:00 2001 From: Shridhar Goel Date: Mon, 24 Jun 2019 19:14:38 +0530 Subject: [PATCH 07/36] chore: Move 'My Events' to the first position in navigation drawer (#1749) --- app/src/main/res/menu/activity_main_drawer.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 9bd7b43de..3e0f53e10 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -1,6 +1,13 @@ + + + + - - - - Date: Mon, 24 Jun 2019 23:06:57 +0530 Subject: [PATCH 08/36] feat: Implement Developer Mode (#1741) --- app/build.gradle | 2 - .../core/main/FragmentNavigator.java | 8 +-- .../organizer/core/main/MainActivity.java | 42 +++++++++++--- .../core/settings/SettingsFragment.java | 57 +++++++++++++++++++ .../main/res/menu/activity_main_drawer.xml | 42 ++++++++------ app/src/main/res/values/strings.xml | 10 ++++ app/src/main/res/xml/preferences.xml | 5 ++ 7 files changed, 135 insertions(+), 31 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f3adaa8ff..34a9421f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,13 +40,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "String", "DEFAULT_BASE_URL", '"https://api.eventyay.com/v1/"' - buildConfigField 'Boolean', 'HIDE_DRAWER_ITEMS', 'true' resValue "string", "default_base_url", '"https://api.eventyay.com/v1/"' } debug { testCoverageEnabled = true buildConfigField "String", "DEFAULT_BASE_URL", '"https://open-event-api-dev.herokuapp.com/v1/"' - buildConfigField 'Boolean', 'HIDE_DRAWER_ITEMS', 'false' resValue "string", "default_base_url", '"https://open-event-api-dev.herokuapp.com/v1/"' } } diff --git a/app/src/main/java/com/eventyay/organizer/core/main/FragmentNavigator.java b/app/src/main/java/com/eventyay/organizer/core/main/FragmentNavigator.java index 0de990b09..aba2b2ee4 100644 --- a/app/src/main/java/com/eventyay/organizer/core/main/FragmentNavigator.java +++ b/app/src/main/java/com/eventyay/organizer/core/main/FragmentNavigator.java @@ -69,9 +69,9 @@ void loadFragment(int navItemId) { case R.id.nav_dashboard: fragment = EventDashboardFragment.newInstance(eventId); break; - /*case R.id.nav_sell: + case R.id.nav_sell: fragment = CreateOrderFragment.newInstance(eventId); - break;*/ + break; case R.id.nav_attendees: fragment = AttendeesFragment.newInstance(eventId); break; @@ -90,7 +90,7 @@ void loadFragment(int navItemId) { case R.id.nav_settings: fragment = SettingsFragment.newInstance(); break; - /*case R.id.nav_faq: + case R.id.nav_faq: fragment = FaqListFragment.newInstance(eventId); break; case R.id.nav_feedback: @@ -110,7 +110,7 @@ void loadFragment(int navItemId) { break; case R.id.nav_speakers_call: fragment = SpeakersCallFragment.newInstance(eventId); - break;*/ + break; case R.id.nav_share: fragment = ShareEventFragment.newInstance(eventId); break; diff --git a/app/src/main/java/com/eventyay/organizer/core/main/MainActivity.java b/app/src/main/java/com/eventyay/organizer/core/main/MainActivity.java index c19b61b9f..0b1cbb1fa 100644 --- a/app/src/main/java/com/eventyay/organizer/core/main/MainActivity.java +++ b/app/src/main/java/com/eventyay/organizer/core/main/MainActivity.java @@ -4,8 +4,12 @@ import androidx.lifecycle.ViewModelProviders; import android.content.Intent; import androidx.databinding.DataBindingUtil; + +import android.content.SharedPreferences; import android.os.Bundle; import androidx.annotation.NonNull; + +import com.eventyay.organizer.common.Constants; import com.google.android.material.navigation.NavigationView; import androidx.fragment.app.Fragment; import androidx.core.view.GravityCompat; @@ -15,7 +19,6 @@ import android.view.MenuItem; import android.view.View; -import com.eventyay.organizer.BuildConfig; import com.eventyay.organizer.R; import com.eventyay.organizer.core.auth.AuthActivity; import com.eventyay.organizer.core.organizer.detail.OrganizerDetailActivity; @@ -39,15 +42,26 @@ public class MainActivity extends AppCompatActivity implements public static final String EVENT_KEY = "event"; private long eventId = -1; + private final List drawerItems = Arrays.asList( - /*R.id.nav_feedback, + R.id.nav_dashboard, + R.id.nav_attendees, + R.id.nav_share, + R.id.nav_about_event, + R.id.nav_event_settings); + + private final List drawerExtraItems = Arrays.asList( + R.id.nav_sell, + R.id.nav_orders, + R.id.nav_tickets, + R.id.nav_edit_event, + R.id.nav_feedback, R.id.nav_faq, R.id.nav_track, R.id.nav_sponsor, R.id.nav_speaker, - R.id.nav_speakers_call,*/ - R.id.nav_about_event - /*R.id.nav_roles*/); + R.id.nav_speakers_call, + R.id.nav_roles); @Inject ViewModelProvider.Factory viewModelFactory; @@ -63,6 +77,8 @@ public class MainActivity extends AppCompatActivity implements private OrganizerViewModel organizerViewModel; private EventViewModel eventViewModel; + private SharedPreferences sharedPreferences; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -87,6 +103,8 @@ protected void onCreate(Bundle savedInstanceState) { drawerNavigator = new DrawerNavigator(this, fragmentNavigator, organizerViewModel); headerBinding.profile.setOnClickListener(view -> startActivity(new Intent(this, OrganizerDetailActivity.class))); + + sharedPreferences = getSharedPreferences(Constants.FOSS_PREFS, MODE_PRIVATE); } @Override @@ -141,11 +159,17 @@ public void onDrawerClosed(View drawerView) { public void setEventId(long eventId) { this.eventId = eventId; fragmentNavigator.setEventId(eventId); - binding.navView.getMenu().setGroupVisible(R.id.subMenu, true); - if (BuildConfig.HIDE_DRAWER_ITEMS) { - for (Integer itemId : drawerItems) { - binding.navView.getMenu().findItem(itemId).setVisible(false); + for (Integer itemId : drawerItems) { + binding.navView.getMenu().findItem(itemId).setVisible(true); + } + + boolean isDeveloperModeEnabled = sharedPreferences.getBoolean( + getString(R.string.developer_mode_key), false); + + if (isDeveloperModeEnabled) { + for (Integer itemId : drawerExtraItems) { + binding.navView.getMenu().findItem(itemId).setVisible(true); } } } diff --git a/app/src/main/java/com/eventyay/organizer/core/settings/SettingsFragment.java b/app/src/main/java/com/eventyay/organizer/core/settings/SettingsFragment.java index 54c59428e..e9eb04b4b 100644 --- a/app/src/main/java/com/eventyay/organizer/core/settings/SettingsFragment.java +++ b/app/src/main/java/com/eventyay/organizer/core/settings/SettingsFragment.java @@ -1,16 +1,21 @@ package com.eventyay.organizer.core.settings; +import android.app.AlertDialog; import android.content.ActivityNotFoundException; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; + import androidx.annotation.Nullable; import androidx.fragment.app.FragmentTransaction; import androidx.preference.PreferenceManager; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.eventyay.organizer.core.main.MainActivity; import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat; import com.eventyay.organizer.BuildConfig; @@ -23,6 +28,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { private static final String VERSION = "Version"; private static final String GROSS_SALES = "Gross Sales"; private static final String NET_SALES = "Net Sales"; + public static boolean isDeveloperModeEnabled; private final AcknowledgementDecider acknowledgementDecider = new AcknowledgementDecider(); private PreferenceManager manager; @@ -94,6 +100,51 @@ public void onCreatePreferencesFix(@Nullable Bundle bundle, String rootKey) { acknowledgementDecider.openAcknowledgementsSection(getActivity()); return true; }); + + findPreference("developer_mode").setOnPreferenceClickListener(preference -> { + isDeveloperModeEnabled = manager.getSharedPreferences().getBoolean( + getString(R.string.developer_mode_key), false); + + if (!isDeveloperModeEnabled) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.activate_developer_mode); + builder.setMessage(R.string.developer_mode_activation_message); + builder.setPositiveButton(R.string.yes_take_chances, + (dialog, which) -> { + toggleDeveloperMode(); + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + }); + builder.setNegativeButton(R.string.no_stay_safe, + (dialog, which) -> builder.show().dismiss()); + + AlertDialog dialog = builder.create(); + dialog.show(); + + dialog.getButton(DialogInterface.BUTTON_POSITIVE) + .setTextColor(getResources().getColor(R.color.red_500)); + dialog.getButton(DialogInterface.BUTTON_NEGATIVE) + .setTextColor(getResources().getColor(R.color.green_500)); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.deactivate_developer_mode); + builder.setMessage(R.string.developer_mode_deactivation_message); + builder.setPositiveButton(R.string.yes, + (dialog, which) -> { + toggleDeveloperMode(); + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + }); + builder.setNegativeButton(R.string.no, + (dialog, which) -> builder.show().dismiss()); + + builder.show(); + } + + return true; + }); } public void setSalesDataSummary() { @@ -108,6 +159,12 @@ public void setSalesDataSummary() { findPreference(getString(R.string.sales_data_display_key)).setSummary(salesData); } + public void toggleDeveloperMode() { + isDeveloperModeEnabled = !isDeveloperModeEnabled; + manager.getSharedPreferences().edit().putBoolean( + getString(R.string.developer_mode_key), isDeveloperModeEnabled).apply(); + } + @Override public void onResume() { super.onResume(); diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index 3e0f53e10..92c34a57a 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -14,10 +14,11 @@ android:icon="@drawable/ic_dashboard" android:title="@string/dashboard" /> - + android:title="@string/sell" + android:visible="false" /> + android:title="@string/orders" + android:visible="false" /> + android:checkable="false" + android:visible="false" /> + android:title="@string/tickets" + android:visible="false" /> - + android:title="@string/speakers_call" + android:visible="false" /> + android:checkable="false" /> + android:checkable="true" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e32850f36..6927eba8c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -390,6 +390,16 @@ Notifications Use another URL Use default URL + Developer Mode + developer_mode + Activate Developer Mode + You are activating Developer Mode. This mode will give you access to unreleased app features. These features are unstable and could potentially result in unwanted changes in your event or data loss. Use with care! Do you really want to activate developer mode? + Deactivate Developer Mode + You are deactivating Developer Mode. This will hide the unreleased app features. Do you want to continue? + Yes, I am taking my chances + No, I want to stay safe + Yes + No Africa/Abidjan diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 944e2e73f..995274705 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -50,6 +50,11 @@ + + From 1d51c856525e923be4317def8ff9c01591f953ec Mon Sep 17 00:00:00 2001 From: Shridhar Goel Date: Mon, 24 Jun 2019 23:45:17 +0530 Subject: [PATCH 09/36] feat: Implement Places Autocomplete for location of events (#1737) --- app/build.gradle | 4 +- .../event/create/EventDetailsStepOne.java | 0 .../event/create/UpdateEventFragment.java | 0 .../fdroid/res/layout/event_create_form.xml | 942 ++++++++++++++++++ .../res/layout/event_details_step_one.xml | 144 +-- .../event/create/EventDetailsStepOne.java | 133 +++ .../core/event/create/LocationPicker.java | 49 - .../event/create/UpdateEventFragment.java | 404 ++++++++ .../res/layout/event_create_form.xml | 74 +- .../res/layout/event_details_step_one.xml | 244 +++++ versions.gradle | 2 + 11 files changed, 1808 insertions(+), 188 deletions(-) rename app/src/{main => fdroid}/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java (100%) rename app/src/{main => fdroid}/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java (100%) create mode 100644 app/src/fdroid/res/layout/event_create_form.xml rename app/src/{main => fdroid}/res/layout/event_details_step_one.xml (71%) create mode 100644 app/src/playStore/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java delete mode 100644 app/src/playStore/java/com/eventyay/organizer/core/event/create/LocationPicker.java create mode 100644 app/src/playStore/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java rename app/src/{main => playStore}/res/layout/event_create_form.xml (92%) create mode 100644 app/src/playStore/res/layout/event_details_step_one.xml diff --git a/app/build.gradle b/app/build.gradle index 34a9421f1..c99ecd526 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -125,8 +125,8 @@ dependencies { //implementation "androidx.media:media:1.1.0-alpha04" // GradleIncompatible Workaround //implementation "androidx.legacy:legacy-support-v4:1.0.0" // GradleIncompatible Workaround annotationProcessor 'androidx.annotation:annotation:1.1.0' // Required for Glide - playStoreImplementation "com.google.android.gms:play-services-vision:${versions.play_services}" - playStoreImplementation "com.google.android.gms:play-services-places:${versions.play_services}" + playStoreImplementation "com.google.android.gms:play-services-vision:${versions.play_services_vision}" + playStoreImplementation "com.google.android.libraries.places:places:${versions.places}" implementation 'androidx.constraintlayout:constraintlayout:1.1.3' // Misc diff --git a/app/src/main/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java similarity index 100% rename from app/src/main/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java rename to app/src/fdroid/java/com/eventyay/organizer/core/event/create/EventDetailsStepOne.java diff --git a/app/src/main/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java b/app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java similarity index 100% rename from app/src/main/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java rename to app/src/fdroid/java/com/eventyay/organizer/core/event/create/UpdateEventFragment.java diff --git a/app/src/fdroid/res/layout/event_create_form.xml b/app/src/fdroid/res/layout/event_create_form.xml new file mode 100644 index 000000000..498b147b9 --- /dev/null +++ b/app/src/fdroid/res/layout/event_create_form.xml @@ -0,0 +1,942 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +