/*
* Copyright 2015 - 2016 Hauke Oldsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.gebatzens.sia;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.CalendarContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatDelegate;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import com.google.firebase.messaging.FirebaseMessaging;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;
import de.gebatzens.sia.data.Exams;
import de.gebatzens.sia.data.Filter;
import de.gebatzens.sia.data.Subst;
import de.gebatzens.sia.data.Mensa;
import de.gebatzens.sia.data.News;
import de.gebatzens.sia.data.StaticData;
import de.gebatzens.sia.fragment.RemoteDataFragment;
import de.gebatzens.sia.fragment.SubstFragment;
public class SIAApp extends Application {
public static final int UPDATE_DISABLE = 0, UPDATE_WLAN = 1, UPDATE_ALL = 2;
public static SIAApp SIA_APP;
public MainActivity activity;
public SiaAPI api;
public School school;
public SharedPreferences preferences;
public Filter.FilterList filters = new Filter.FilterList();
public HashMap<String, String> subjects = new HashMap<>();
public LifecycleHandler lifecycle;
public WearDataProvider wearData;
@Override
public void onCreate() {
super.onCreate();
SIA_APP = this;
api = new SiaAPI();
preferences = PreferenceManager.getDefaultSharedPreferences(this);
//SIABroadcast.createAlarm(this, true);
filters = FilterActivity.loadFilter();
loadSubjectMap();
School.loadList();
//copy school: this is required because SetupActivity might change the school in background which leads to inconsistent states.
school = School.getBySID(preferences.getString("sid", null));
if(school != null) {
school = new School(school);
}
loadSavedData();
lifecycle = new LifecycleHandler();
registerActivityLifecycleCallbacks(lifecycle);
//1.3 upgrade
if(preferences.contains("notification_led")) {
preferences.edit().putString("notification_led_color", preferences.getBoolean("notification_led", true) ? "#2196F3" : "#000000").remove("notification_led").apply();
}
if(school != null) {
FirebaseMessaging.getInstance().subscribeToTopic("sia_sid_" + school.sid);
}
FirebaseMessaging.getInstance().subscribeToTopic("news");
wearData = new WearDataProvider();
wearData.updateMainFilters(filters.including);
}
/**
* Loads saved data (if present) for each fragment
*/
private void loadSavedData() {
if(school != null) {
for(FragmentData frag : school.fragments) {
RemoteDataFragment.RemoteData rd = null;
switch(frag.type) {
case PLAN:
rd = new Subst.GGPlans();
break;
case EXAMS:
rd = new Exams();
break;
case NEWS:
rd = new News();
break;
case MENSA:
rd = new Mensa();
break;
case PDF:
rd = new StaticData();
((StaticData) rd).name = frag.params;
break;
}
if(!rd.load())
rd = null;
frag.setData(rd);
}
}
}
private void loadSubjectMap() {
subjects.clear();
String[] array = getResources().getStringArray(R.array.subjects);
for(String s : array) {
String[] parts = s.split("\\|");
String value = parts[parts.length - 1];
for(int i = 0; i < parts.length - 1; i++) {
subjects.put(parts[i], value);
}
}
}
public void createNotification(int icon, String title, String message, Intent intent, int id, String... strings) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(icon)
.setContentTitle(title)
.setContentText(message);
if (strings.length > 1) {
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
inboxStyle.setBigContentTitle(strings[0]);
boolean b = true;
for (String s : strings) {
if (!b) {
inboxStyle.addLine(s);
}
b = false;
}
mBuilder.setStyle(inboxStyle);
}
mBuilder.setColor(SIAApp.SIA_APP.school.getDarkColor());
if (Color.parseColor(getLedColor()) != Color.BLACK) {
mBuilder.setLights(Color.parseColor(getLedColor()), 1000, 1000);
}
String vibration = preferences.getString("vibration", "off");
switch (vibration) {
case "short":
mBuilder.setVibrate(new long[]{0, 500});
break;
case "default":
mBuilder.setVibrate(new long[]{0, 200, 200, 200, 200, 200});
break;
case "long":
mBuilder.setVibrate(new long[]{0, 200, 100, 400, 200, 800, 300, 1000, 1200, 200});
break;
}
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(id, mBuilder.build());
}
public long getCalendarId() {
//TODO select default calendar and try catch
boolean hasPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_GRANTED;
if(!hasPermission && lifecycle.isAppInForeground()) {
ActivityCompat.requestPermissions(activity, new String[]{ Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CALENDAR }, 1);
return -2;
} else if(hasPermission) {
String[] projection = new String[]{CalendarContract.Calendars._ID, CalendarContract.Calendars.NAME};
Cursor cursor = getContentResolver().query(CalendarContract.Calendars.CONTENT_URI, projection, null, null, null);
if (cursor.moveToFirst()) {
long id = cursor.getLong(0);
cursor.close();
return id;
}
}
Log.w("ggvp", "No calendar available");
return -1;
}
@SuppressWarnings("MissingPermission")
public void addToCalendar(long calId, Date date, String title) {
//TODO getTime should be enough
long start = date.getTime() + 1000 * 60 * 60 * 6;
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.DTSTART, start);
values.put(CalendarContract.Events.DTEND, start);
values.put(CalendarContract.Events.CALENDAR_ID, calId);
values.put(CalendarContract.Events.ACCESS_LEVEL, CalendarContract.Events.ACCESS_DEFAULT);
values.put(CalendarContract.Events.ALL_DAY, 1);
values.put(CalendarContract.Events.TITLE, title);
values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
getContentResolver().insert(CalendarContract.Events.CONTENT_URI, values).getLastPathSegment();
}
public int translateUpdateType(String s) {
switch (s) {
case "disable":
return UPDATE_DISABLE;
case "wifi":
return UPDATE_WLAN;
case "all":
return UPDATE_ALL;
}
return UPDATE_DISABLE;
}
public int getUpdateType() {
if(preferences.getBoolean("notifications", true))
return translateUpdateType(preferences.getString("background_updates", "all"));
else
return UPDATE_DISABLE;
}
public int getFragmentIndex() {
return preferences.getInt("fragindex", 0);
}
public void setFragmentIndex(int index) {
preferences.edit().putInt("fragindex", index).apply();
}
public String getLedColor() {
return preferences.getString("notification_led_color", "#2196F3");
}
public void setLedColor(String ledColor) {
preferences.edit().putString("notification_led_color", ledColor).apply();
}
public String getCustomThemeName() {
return preferences.getString("customTheme", null);
}
public void setCustomThemeName(String customTheme) {
preferences.edit().putString("customTheme", customTheme).apply();
}
public int getThemeMode() {
return preferences.getInt("themeMode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
public void setThemeMode(int e) {
preferences.edit().putInt("themeMode", e).apply();
AppCompatDelegate.setDefaultNightMode(e);
}
public void setSchool(String sid) {
preferences.edit().putString("sid", sid).apply();
if(sid == null)
return;
School b = school;
school = School.getBySID(sid);
if(school != null) {
school = new School(school);
}
if(activity != null && b != null && !b.equals(school)) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.recreate();
}
});
}
}
public String getCurrentThemeName() {
String s = getCustomThemeName();
return s == null ? school.themeName : s;
}
public void refreshAsync(final Runnable finished, final boolean updateFragments, final FragmentData frag) {
new Thread() {
@Override
public void run() {
if(activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.resetShareToolbar();
}
});
}
boolean update = false;
switch(frag.type) {
case PLAN:
Subst.GGPlans oldPlans = (Subst.GGPlans) frag.getData();
final Subst.GGPlans plans = api.getPlans(updateFragments);
frag.setData(plans);
if(plans.throwable == null)
plans.save();
if(activity != null && (oldPlans == null || plans.size() != oldPlans.size())) {
update = true;
} else if (updateFragments) {
update = oldPlans.shouldRecreateView(plans);
}
if(activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Fragment frag = activity.mContent;
if(frag instanceof SubstFragment)
((SubstFragment) frag).updateTime(plans.loadDate);
}
});
}
break;
case NEWS:
News on = (News) frag.getData();
News news = api.getNews(updateFragments);
frag.setData(news);
if(news.throwable == null)
on.save();
update = on == null || !on.equals(news);
break;
case MENSA:
Mensa om = (Mensa) frag.getData();
Mensa mensa = api.getMensa(updateFragments);
frag.setData(mensa);
if(mensa.throwable == null)
mensa.save();
update = om == null || !om.equals(mensa);
break;
case EXAMS:
Exams newExams = api.getExams(updateFragments);
Exams exams = (Exams) frag.getData();
if(exams != null) {
exams.reuseSelected(newExams);
}
exams = newExams;
frag.setData(exams);
if(exams.throwable == null)
exams.save();
update = true;
break;
case PDF:
StaticData od = (StaticData) frag.getData();
StaticData data = api.downloadStaticFile(frag.getParams(), updateFragments);
frag.setData(data);
if(data.throwable == null)
data.save();
update = data.throwable == null;
break;
}
if(activity != null && updateFragments && update)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.mContent.updateFragment();
}
});
if(activity != null && finished != null)
activity.runOnUiThread(finished);
}
}.start();
}
public String getSeasonTheme() {
int m = Calendar.getInstance().get(Calendar.MONTH);
switch(m) {
case Calendar.OCTOBER:
case Calendar.NOVEMBER:
case Calendar.DECEMBER:
case Calendar.JANUARY:
case Calendar.FEBRUARY:
case Calendar.MARCH:
return "Winter";
default:
return "Summer";
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void setStatusBarColorTransparent(Window w) {
w.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
w.setStatusBarColor(ContextCompat.getColor(this, R.color.transparent));
}
public static String deleteNonAlphanumeric(String s) {
return s.replaceAll("\\W", "").toLowerCase();
}
}