Commit 0d28554a authored by Robert Hitt's avatar Robert Hitt
Browse files

Added support for special schedules

- Also reorganized the structure of the program a bit
parent 76d0e27e
......@@ -10,7 +10,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ui.activities.MainActivity"
<activity android:name=".views.activities.MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
......@@ -19,18 +19,18 @@
</intent-filter>
</activity>
<activity android:name=".ui.activities.DetailActivity"
android:parentActivityName=".ui.activities.MainActivity">
<activity android:name=".views.activities.DetailActivity"
android:parentActivityName=".views.activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity"/>
android:value=".views.activities.MainActivity"/>
</activity>
<activity android:name=".ui.activities.AboutActivity"
android:parentActivityName=".ui.activities.MainActivity">
<activity android:name=".views.activities.AboutActivity"
android:parentActivityName=".views.activities.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity"/>
android:value=".views.activities.MainActivity"/>
</activity>
</application>
......
......@@ -9,10 +9,12 @@ import io.realm.annotations.PrimaryKey;
public class Facility extends RealmObject {
public Facility(String name, String location, MainSchedule mainSchedule,
boolean isOpen, boolean isFavorited) {
RealmList<SpecialSchedule> specialSchedules, boolean isOpen,
boolean isFavorited) {
mName = name;
mLocation = location;
mMainSchedule = mainSchedule;
mSpecialSchedules = specialSchedules;
this.isOpen = isOpen;
this.isFavorited = isFavorited;
}
......@@ -30,10 +32,22 @@ public class Facility extends RealmObject {
@SerializedName("main_schedule")
private MainSchedule mMainSchedule;
@SerializedName("special_schedules")
private RealmList<SpecialSchedule> mSpecialSchedules;
private boolean isOpen;
private boolean isFavorited;
public RealmList<SpecialSchedule> getSpecialSchedules() {
return mSpecialSchedules;
}
public void setSpecialSchedules(RealmList<SpecialSchedule> specialSchedules) {
mSpecialSchedules = specialSchedules;
}
public boolean isOpen() {
return isOpen;
}
......
......@@ -7,8 +7,10 @@ import io.realm.RealmObject;
public class MainSchedule extends RealmObject {
public MainSchedule(RealmList<OpenTimes> openTimesList) {
public MainSchedule(RealmList<OpenTimes> openTimesList, String validStart, String validEnd) {
mOpenTimesList = openTimesList;
this.validStart = validStart;
this.validEnd = validEnd;
}
public MainSchedule() {
......@@ -17,6 +19,27 @@ public class MainSchedule extends RealmObject {
@SerializedName("open_times")
private RealmList<OpenTimes> mOpenTimesList;
@SerializedName("valid_start")
private String validStart;
@SerializedName("valid_end")
private String validEnd;
public String getValidStart() {
return validStart;
}
public void setValidStart(String validStart) {
this.validStart = validStart;
}
public String getValidEnd() {
return validEnd;
}
public void setValidEnd(String validEnd) {
this.validEnd = validEnd;
}
public RealmList<OpenTimes> getOpenTimesList() {
return mOpenTimesList;
}
......
package srct.whatsopen.model;
import com.google.gson.annotations.SerializedName;
import io.realm.RealmList;
import io.realm.RealmObject;
public class SpecialSchedule extends RealmObject {
public SpecialSchedule(RealmList<OpenTimes> openTimesList, String validStart, String validEnd) {
mOpenTimesList = openTimesList;
this.validStart = validStart;
this.validEnd = validEnd;
}
public SpecialSchedule() {
}
@SerializedName("open_times")
private RealmList<OpenTimes> mOpenTimesList;
@SerializedName("valid_start")
private String validStart;
@SerializedName("valid_end")
private String validEnd;
public String getValidStart() {
return validStart;
}
public void setValidStart(String validStart) {
this.validStart = validStart;
}
public String getValidEnd() {
return validEnd;
}
public void setValidEnd(String validEnd) {
this.validEnd = validEnd;
}
public RealmList<OpenTimes> getOpenTimesList() {
return mOpenTimesList;
}
public void setOpenTimesList(RealmList<OpenTimes> openTimesList) {
mOpenTimesList = openTimesList;
}
}
package srct.whatsopen.ui.presenters;
package srct.whatsopen.presenters;
import android.content.SharedPreferences;
......@@ -13,7 +13,8 @@ import io.realm.Realm;
import io.realm.RealmList;
import srct.whatsopen.model.Facility;
import srct.whatsopen.model.OpenTimes;
import srct.whatsopen.ui.FacilityView;
import srct.whatsopen.model.SpecialSchedule;
import srct.whatsopen.views.FacilityView;
public class FacilityPresenter {
......@@ -42,7 +43,7 @@ public class FacilityPresenter {
final String facilityName = facility.getName();
realm.executeTransactionAsync(bgRealm -> {
// have to requery for the object as it was created on a separate thread
// have to re-query for the object as it was created on a separate thread
Facility f = bgRealm.where(Facility.class)
.equalTo("mName", facilityName).findFirst();
......@@ -56,7 +57,7 @@ public class FacilityPresenter {
// Finds the next time the facility closes or opens and returns it
public String getStatusDuration(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
RealmList<OpenTimes> openTimesList = getActiveSchedule(facility, now);
if(openTimesList.size() == 0)
return "No open time on schedule";
......@@ -150,8 +151,8 @@ public class FacilityPresenter {
}
// Parses the schedule into an HTML string
public String getSchedule(Facility facility) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
public String getSchedule(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = getActiveSchedule(facility, now);
if(openTimesList.size() == 0)
return "No schedule available";
......@@ -172,4 +173,29 @@ public class FacilityPresenter {
return scheduleString.toString();
}
// Returns the active schedule given the current date
private RealmList<OpenTimes> getActiveSchedule(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date currentDate = now.getTime();
for(SpecialSchedule s : facility.getSpecialSchedules()) {
Date startDate = sdf.parse(s.getValidStart());
Date endDate = sdf.parse(s.getValidEnd());
if(currentDate.compareTo(startDate) >= 0 && currentDate.compareTo(endDate) <= 0) {
openTimesList = s.getOpenTimesList();
return openTimesList;
}
}
} catch (ParseException pe) {
pe.printStackTrace();
return null;
}
return openTimesList;
}
}
package srct.whatsopen.ui.presenters;
package srct.whatsopen.presenters;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import java.text.ParseException;
import java.text.SimpleDateFormat;
......@@ -21,9 +22,10 @@ import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import srct.whatsopen.model.Facility;
import srct.whatsopen.model.OpenTimes;
import srct.whatsopen.service.WhatsOpenClient;
import srct.whatsopen.model.SpecialSchedule;
import srct.whatsopen.service.WhatsOpenService;
import srct.whatsopen.ui.activities.MainActivity;
import srct.whatsopen.service.WhatsOpenApi;
import srct.whatsopen.views.activities.MainActivity;
public class MainPresenter {
......@@ -44,8 +46,8 @@ public class MainPresenter {
public void loadFacilities() {
mMainView.showProgressBar();
// Get WhatsOpenClient singleton
WhatsOpenService service = WhatsOpenClient.getInstance();
// Get WhatsOpenApi singleton
WhatsOpenApi service = WhatsOpenService.getInstance();
Observable<List<Facility>> call = service.facilityList();
call.subscribeOn(Schedulers.io())
......@@ -62,6 +64,7 @@ public class MainPresenter {
public void onError(Throwable e) {
if(mMainView != null)
mMainView.dismissProgressBar();
Log.d("Network bad", e.getMessage());
// should probably show some error message
}
@Override
......@@ -92,7 +95,7 @@ public class MainPresenter {
// Uses the device time to determine which facilities should be open
public boolean getOpenStatus(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
RealmList<OpenTimes> openTimesList = getActiveSchedule(facility, now);
// have to mess with the current day value, as Calender.DAY_OF_WEEK
// starts with Sunday as 1 and the Whats Open Api starts with Monday at 0
......@@ -124,4 +127,29 @@ public class MainPresenter {
return false;
}
}
// Returns the active schedule given the current date
private RealmList<OpenTimes> getActiveSchedule(Facility facility, Calendar now) {
RealmList<OpenTimes> openTimesList = facility.getMainSchedule().getOpenTimesList();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date currentDate = now.getTime();
for(SpecialSchedule s : facility.getSpecialSchedules()) {
Date startDate = sdf.parse(s.getValidStart());
Date endDate = sdf.parse(s.getValidEnd());
if(currentDate.compareTo(startDate) >= 0 && currentDate.compareTo(endDate) <= 0) {
openTimesList = s.getOpenTimesList();
return openTimesList;
}
}
} catch (ParseException pe) {
pe.printStackTrace();
return null;
}
return openTimesList;
}
}
package srct.whatsopen.service;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import rx.Observable;
import srct.whatsopen.model.Facility;
// Interface for Retrofit's Http request
public interface WhatsOpenApi {
@GET("facilities")
Observable<List<Facility>> facilityList();
}
package srct.whatsopen.service;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.realm.RealmObject;
import io.realm.rx.RxObservableFactory;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Singleton for Retrofit instance
*/
public class WhatsOpenClient {
private static volatile Retrofit sRetrofit = null;
private static WhatsOpenService mService;
private static final String BASE_URL = "https://whatsopen.gmu.edu/api/";
public WhatsOpenClient() {
}
// Returns a singleton of WhatsOpenService
public static WhatsOpenService getInstance() {
if(mService == null) {
synchronized (WhatsOpenClient.class) {
if(mService == null) {
mService = getRetrofit().create(WhatsOpenService.class);
}
}
}
return mService;
}
// Configures Retrofit's JSON parsing logic
private synchronized static Retrofit getRetrofit() {
if(sRetrofit == null) {
synchronized(WhatsOpenClient.class) {
if(sRetrofit == null) {
Gson gson = new GsonBuilder()
// Ensures Retrofit plays nicely with Realm
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredClass().equals(RealmObject.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();
sRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
}
}
return sRetrofit;
}
}
package srct.whatsopen.service;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import rx.Observable;
import srct.whatsopen.model.Facility;
import io.realm.RealmObject;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
// Interface for Retrofit's Http request
public interface WhatsOpenService {
/**
* Singleton for Retrofit instance
*/
public class WhatsOpenService {
private static volatile Retrofit sRetrofit = null;
private static WhatsOpenApi mService;
private static final String BASE_URL = "https://whatsopen.gmu.edu/api/";
public WhatsOpenService() {
}
// Returns a singleton of WhatsOpenApi
public static WhatsOpenApi getInstance() {
if(mService == null) {
synchronized (WhatsOpenService.class) {
if(mService == null) {
mService = getRetrofit().create(WhatsOpenApi.class);
}
}
}
return mService;
}
// Configures Retrofit's JSON parsing logic
private synchronized static Retrofit getRetrofit() {
if(sRetrofit == null) {
synchronized(WhatsOpenService.class) {
if(sRetrofit == null) {
Gson gson = new GsonBuilder()
// Ensures Retrofit plays nicely with Realm
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaredClass().equals(RealmObject.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.create();
sRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
}
}
return sRetrofit;
}
@GET("facilities")
Observable<List<Facility>> facilityList();
}
package srct.whatsopen.ui;
package srct.whatsopen.views;
import android.content.Context;
......
package srct.whatsopen.ui;
package srct.whatsopen.views;
public interface MainView {
......
package srct.whatsopen.ui.activities;
package srct.whatsopen.views.activities;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import com.danielstone.materialaboutlibrary.BuildConfig;
import com.danielstone.materialaboutlibrary.MaterialAboutActivity;
import com.danielstone.materialaboutlibrary.model.MaterialAboutActionItem;
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard;
......
package srct.whatsopen.ui.activities;
package srct.whatsopen.views.activities;
import android.content.Context;
import android.os.Bundle;
......@@ -17,8 +17,8 @@ import butterknife.ButterKnife;
import io.realm.Realm;
import srct.whatsopen.R;
import srct.whatsopen.model.Facility;
import srct.whatsopen.ui.FacilityView;
import srct.whatsopen.ui.presenters.FacilityPresenter;
import srct.whatsopen.views.FacilityView;
import srct.whatsopen.presenters.FacilityPresenter;
public class DetailActivity extends AppCompatActivity implements FacilityView{
......@@ -124,6 +124,7 @@ public class DetailActivity extends AppCompatActivity implements FacilityView{
locationTextView.setText(mFacility.getLocation());
scheduleTextView.setText(Html.fromHtml(mPresenter.getSchedule(mFacility)));
scheduleTextView.setText(Html.fromHtml(mPresenter
.getSchedule(mFacility, Calendar.getInstance())));
}
}
package srct.whatsopen.ui.activities;
package srct.whatsopen.views.activities;
import android.content.Intent;
import android.support.v4.view.ViewPager;
......@@ -8,7 +8,6 @@ import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.LinearLayout;
import com.astuetz.PagerSlidingTabStrip;
......@@ -19,9 +18,9 @@ import fr.castorflex.android.circularprogressbar.CircularProgressBar;
import io.realm.Realm;
import srct.whatsopen.R;
import srct.whatsopen.ui.MainView;
import srct.whatsopen.ui.presenters.MainPresenter;
import srct.whatsopen.ui.adapters.FacilityListFragmentPagerAdapter;
import srct.whatsopen.views.MainView;
import srct.whatsopen.presenters.MainPresenter;
import srct.whatsopen.views.adapters.FacilityListFragmentPagerAdapter;
public class MainActivity extends AppCompatActivity implements MainView {
......
package srct.whatsopen.ui.adapters;
package srct.whatsopen.views.adapters;
import android.content.Context;
......@@ -19,9 +19,9 @@ import io.realm.OrderedRealmCollection;
import io.realm.RealmRecyclerViewAdapter;
import srct.whatsopen.R;
import srct.whatsopen.model.Facility;
import srct.whatsopen.ui.FacilityView;
import srct.whatsopen.ui.activities.DetailActivity;
import srct.whatsopen.ui.presenters.FacilityPresenter;
import srct.whatsopen.views.FacilityView;
import srct.whatsopen.views.activities.DetailActivity;
import srct.whatsopen.presenters.FacilityPresenter;
/**
* Basic RecyclerView boilerplate, with some added Realm stuff
......
package srct.whatsopen.ui.adapters;
package srct.whatsopen.views.adapters;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;