diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 53d9c8c6..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "hackertracker/src/main/assets/database"]
- path = hackertracker/src/main/assets/database
- url = https://github.com/BeezleLabs/conferences
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..88ea3aa1
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 96cc43ef..00000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf33..00000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 97626ba4..00000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 8d7d28cd..00000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0df579a3..703e5d4b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,41 +1,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 4e176ce2..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddf..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/hackertracker/.gitignore b/app/.gitignore
similarity index 100%
rename from hackertracker/.gitignore
rename to app/.gitignore
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 00000000..d84eca23
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,105 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'maven'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.gms.google-services'
+apply plugin: 'com.google.firebase.crashlytics'
+
+android {
+ compileSdkVersion 29
+ defaultConfig {
+ applicationId "com.shortstack.hackertracker"
+ minSdkVersion 16
+ targetSdkVersion 29
+ versionCode 191
+ versionName "6.2.0"
+ vectorDrawables.useSupportLibrary = true
+ multiDexEnabled true
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ useProguard true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+
+ // Support Libraries
+ implementation 'com.google.android.material:material:1.3.0-alpha01'
+ implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
+ implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha03'
+ implementation 'androidx.cardview:cardview:1.0.0'
+ implementation 'androidx.preference:preference:1.1.1'
+
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+
+ // Koin
+ implementation 'org.koin:koin-android:2.1.6'
+ implementation 'org.koin:koin-androidx-scope:2.1.6'
+ implementation 'org.koin:koin-androidx-viewmodel:2.1.6'
+
+ // Arch
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0-alpha04"
+ implementation "android.arch.work:work-runtime-ktx:1.0.1"
+ implementation "android.arch.work:work-firebase:1.0.0-alpha11"
+
+ // Retrofit
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.7.0'
+ implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
+ implementation 'com.squareup.okhttp3:okhttp:3.14.9'
+ implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
+
+ // Pretty Logger
+ implementation 'com.orhanobut:logger:1.15'
+ // PDF Viewer
+ implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'
+ // Reviews
+ implementation 'com.github.stkent:amplify:2.2.0'
+ // Firebase Job Dispatcher
+ implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
+
+ implementation 'com.github.Advice-Dog:timehop:master-SNAPSHOT'
+
+
+ implementation "com.hendraanggrian.material:collapsingtoolbarlayout-subtitle:1.0.0-beta01"
+
+ // Firebase
+ implementation 'com.google.firebase:firebase-analytics:17.4.4'
+ implementation 'com.google.firebase:firebase-firestore:21.5.0'
+ implementation 'com.google.firebase:firebase-storage:19.1.1'
+ implementation 'com.google.firebase:firebase-auth:19.3.2'
+ implementation 'com.google.firebase:firebase-messaging:20.2.3'
+ implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
+ implementation 'com.google.guava:guava:27.0.1-android'
+
+
+ // Coroutines
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.2.1'
+
+ implementation 'androidx.multidex:multidex:2.0.1'
+
+ testImplementation "junit:junit:4.12"
+ testImplementation "io.mockk:mockk:1.9.3.kotlin12"
+ testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
+ testImplementation 'org.mockito:mockito-inline:2.21.0'
+ testImplementation 'org.koin:koin-test:2.0.0-beta-4'
+}
+
diff --git a/hackertracker/google-services.json b/app/google-services.json
similarity index 100%
rename from hackertracker/google-services.json
rename to app/google-services.json
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 00000000..145c6d91
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,31 @@
+# Preserve annotations, line numbers, and source file names
+-keepattributes *Annotation*
+-keepattributes SourceFile,LineNumberTable
+-keep public class * extends java.lang.Exception
+-keep class com.crashlytics.** { *; }
+-dontwarn com.crashlytics.**
+
+-keepattributes Signature
+-keepattributes Exceptions
+
+-keep class com.shortstack.hackertracker.network.** { *; }
+-keep class com.shortstack.hackertracker.models.** { *; }
+-keep class com.shortstack.hackertracker.ui.themes.** { *; }
+
+-dontwarn com.shortstack.hackertracker.views.**
+
+-keep class com.shortstack.hackertracker.ui.home.HomeFragment { *; }
+-keep class com.shortstack.hackertracker.ui.schedule.ScheduleFragment { *; }
+-keep class com.shortstack.hackertracker.ui.maps.MapsFragment { *; }
+-keep class com.shortstack.hackertracker.ui.information.faq.FAQFragment { *; }
+-keep class com.shortstack.hackertracker.ui.information.vendors.VendorsFragment { *; }
+-keep class com.shortstack.hackertracker.ui.search.SearchFragment { *; }
+-keep class com.shortstack.hackertracker.ui.settings.SettingsFragment { *; }
+
+-keep class android.support.v7.widget.SearchView { *; }
+
+# Parceler configuration
+-keep interface org.parceler.Parcel
+-keep @org.parceler.Parcel class * { *; }
+-keep class **$$Parcelable { *; }
+-keep class org.parceler.Parceler$$Parcels
\ No newline at end of file
diff --git a/hackertracker/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
similarity index 88%
rename from hackertracker/src/main/AndroidManifest.xml
rename to app/src/main/AndroidManifest.xml
index 92feaddd..f0168aea 100644
--- a/hackertracker/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,12 +4,13 @@
android:installLocation="auto">
+
-
+
-
-
-
-
-
-
(val status: Status, val data: T?, val message: String
fun loading(data: T?) = Resource(Status.LOADING, data, null)
- fun init(data: T?) = Resource(Status.NOT_INITIALIZED, data, null)
+ fun init(data: T? = null) = Resource(Status.NOT_INITIALIZED, data, null)
}
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt b/app/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt
new file mode 100644
index 00000000..9f36b590
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt
@@ -0,0 +1,563 @@
+package com.shortstack.hackertracker.database
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.workDataOf
+import com.google.android.gms.tasks.OnCompleteListener
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.auth.FirebaseUser
+import com.google.firebase.firestore.FirebaseFirestore
+import com.google.firebase.firestore.FirebaseFirestoreSettings
+import com.google.firebase.firestore.Query
+import com.google.firebase.iid.FirebaseInstanceId
+import com.google.firebase.storage.FirebaseStorage
+import com.orhanobut.logger.Logger
+import com.shortstack.hackertracker.*
+import com.shortstack.hackertracker.models.firebase.*
+import com.shortstack.hackertracker.models.local.*
+import com.shortstack.hackertracker.network.task.ReminderWorker
+import com.shortstack.hackertracker.utilities.MyClock
+import com.shortstack.hackertracker.utilities.Storage
+import com.shortstack.hackertracker.utilities.now
+import kotlinx.coroutines.tasks.await
+import java.io.File
+import java.util.concurrent.TimeUnit
+
+
+class DatabaseManager(private val preferences: Storage) {
+
+ companion object {
+ private const val CONFERENCES = "conferences"
+
+ private const val USERS = "users"
+ private const val BOOKMARKS = "bookmarks"
+
+ private const val EVENTS = "events"
+ private const val TYPES = "types"
+ private const val FAQS = "faqs"
+ private const val VENDORS = "vendors"
+ private const val SPEAKERS = "speakers"
+ private const val LOCATIONS = "locations"
+ private const val ARTICLES = "articles"
+
+ fun getNextConference(preferred: Int, conferences: List): Conference? {
+ if (preferred != -1) {
+ val pref = conferences.find { it.id == preferred && !it.hasFinished }
+ if (pref != null) return pref
+ }
+
+ val list = conferences.sortedBy { it.startDate }
+
+ val defcon = list.find { it.code == "DEFCON27" }
+ if (defcon?.hasFinished == false) {
+ return defcon
+ }
+
+ return list.firstOrNull { !it.hasFinished }
+ ?: conferences.lastOrNull()
+ }
+ }
+
+ private val code
+ get() = conference.value?.code ?: "DEFCON27"
+
+ private val firestore = FirebaseFirestore.getInstance()
+ private val storage = FirebaseStorage.getInstance()
+ private val auth = FirebaseAuth.getInstance()
+
+
+ val conference = MutableLiveData()
+ val conferences = MutableLiveData>()
+
+ private var user: FirebaseUser? = null
+
+ init {
+ if (!BuildConfig.DEBUG) {
+ val settings = FirebaseFirestoreSettings.Builder()
+ .setPersistenceEnabled(true)
+ .build()
+
+ firestore.firestoreSettings = settings
+ }
+
+ auth.signInAnonymously().addOnCompleteListener {
+ if (it.isSuccessful) {
+ user = it.result?.user ?: return@addOnCompleteListener
+ }
+
+ firestore.collection(CONFERENCES)
+ .get()
+ .addOnCompleteListener {
+ if (it.isSuccessful) {
+ val list = it.result?.toObjects(FirebaseConference::class.java)
+ ?.filter { !it.hidden || App.isDeveloper }
+ ?.map { it.toConference() }
+ ?.sortedBy { it.startDate }
+
+ ?: emptyList()
+
+
+ val con = getNextConference(preferences.preferredConference, list)
+ conference.postValue(con)
+ conferences.postValue(list)
+
+ if (con != null)
+ getFCMToken(con)
+ }
+ }
+ }
+ }
+
+
+ private fun getFCMToken(conference: Conference) {
+ FirebaseInstanceId.getInstance().instanceId
+ .addOnCompleteListener(OnCompleteListener { task ->
+ if (!task.isSuccessful) {
+ Logger.e(task.exception, "Could not get token.")
+ return@OnCompleteListener
+ }
+
+ // Get new Instance ID token
+ val token = task.result?.token
+ Logger.d("Obtained token: $token")
+ updateFirebaseMessagingToken(conference, token)
+ })
+ }
+
+ fun changeConference(id: Int) {
+ preferences.preferredConference = id
+
+ val current = conference.value
+
+ if (current != null) {
+ current.isSelected = false
+ }
+
+ firestore.collection(CONFERENCES)
+ .whereEqualTo("id", id)
+ .get()
+ .addOnCompleteListener {
+ if (it.isSuccessful) {
+ val selected =
+ it.result?.toObjects(FirebaseConference::class.java)?.firstOrNull()
+ ?.toConference()
+ conference.postValue(selected)
+ }
+ }
+ }
+
+ fun getConferences(): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val cons = snapshot?.toObjects(FirebaseConference::class.java)
+ ?.filter { !it.hidden || App.isDeveloper }
+ ?.map { it.toConference() }
+
+ mutableLiveData.postValue(cons)
+ }
+ }
+
+ return mutableLiveData
+ }
+
+ fun getEvents(id: Conference, type: Type?): LiveData> {
+ return getSchedule()
+ }
+
+
+ fun getSchedule(): MutableLiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(EVENTS)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val events = snapshot?.toObjects(FirebaseEvent::class.java)
+ ?.filter { (!it.hidden || App.isDeveloper) }
+ ?.map { it.toEvent() }
+
+ mutableLiveData.postValue(events)
+
+ val id = user?.uid
+ if (id != null) {
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(USERS)
+ .document(id)
+ .collection(BOOKMARKS)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val bookmarks =
+ snapshot?.toObjects(FirebaseBookmark::class.java)
+
+ bookmarks?.forEach { bookmark ->
+ events?.find { it.id.toString() == bookmark.id }
+ ?.isBookmarked = bookmark.value
+ }
+
+ mutableLiveData.postValue(events)
+ }
+ }
+ }
+ }
+ }
+
+ return mutableLiveData
+ }
+
+ fun getTypes(id: Conference): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(id.code)
+ .collection(TYPES)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val types = snapshot?.toObjects(FirebaseType::class.java)?.map { it.toType() }
+ mutableLiveData.postValue(types)
+
+ val id = user?.uid
+ if (id != null) {
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(USERS)
+ .document(id)
+ .collection(TYPES)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val bookmarks =
+ snapshot?.toObjects(FirebaseBookmark::class.java)
+
+ types?.forEach { type ->
+ type.isSelected =
+ bookmarks?.find { type.id.toString() == it.id }?.value
+ ?: false
+ }
+
+ mutableLiveData.postValue(types)
+ }
+ }
+ }
+ }
+ }
+
+ return mutableLiveData
+ }
+
+
+ fun getRecent(): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(EVENTS)
+ .orderBy("updated_timestamp", Query.Direction.DESCENDING)
+ .limit(5)
+ .get()
+ .addOnSuccessListener {
+ val events = it.toObjects(FirebaseEvent::class.java)
+ .filter { !it.hidden || App.isDeveloper }
+ .map { it.toEvent() }
+
+ mutableLiveData.postValue(events)
+ }
+
+ return mutableLiveData
+ }
+
+ fun getArticles(id: Conference? = null): LiveData> {
+ val results = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(id?.code ?: code)
+ .collection(ARTICLES)
+ .orderBy("updated_at", Query.Direction.DESCENDING)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val articles = snapshot?.toObjects(FirebaseArticle::class.java)
+ ?.filter { !it.hidden || App.isDeveloper }
+ ?.map { it.toArticle() } ?: emptyList()
+
+ results.postValue(articles)
+ }
+
+ }
+
+ return results
+ }
+
+ fun getBookmarks(conference: Conference? = null): LiveData> {
+ val result = MutableLiveData>()
+
+ val id = user?.uid ?: return result
+
+ firestore.collection(CONFERENCES)
+ .document(conference?.code ?: code)
+ .collection(EVENTS)
+ .get()
+ .addOnSuccessListener {
+ val events = it.toObjects(FirebaseEvent::class.java)
+ .filter { !it.hidden || App.isDeveloper }
+ .map { it.toEvent() }
+
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(USERS)
+ .document(id)
+ .collection(BOOKMARKS)
+ .get()
+ .addOnSuccessListener {
+ val bookmarks = it.toObjects(FirebaseBookmark::class.java).map { it.id }
+
+ val bookmarked = events.filter { it.id.toString() in bookmarks }.take(3)
+ bookmarked.forEach { it.isBookmarked = true }
+
+ result.postValue(bookmarked)
+
+
+ }
+ }
+
+ return result
+ }
+
+
+ fun getFAQ(id: Conference): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(id.code)
+ .collection(FAQS)
+ .get()
+ .addOnSuccessListener {
+ val faqs = it.toObjects(FirebaseFAQ::class.java)
+ .map { it.toFAQ() }
+ mutableLiveData.postValue(faqs)
+ }
+
+ return mutableLiveData
+ }
+
+ fun getLocations(id: Conference? = null): MutableLiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(id?.code ?: code)
+ .collection(LOCATIONS)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val list =
+ snapshot?.toObjects(FirebaseLocation::class.java)?.map { it.toLocation() }
+ mutableLiveData.postValue(list)
+ }
+ }
+
+ return mutableLiveData
+ }
+
+ fun getVendors(conference: Conference): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(conference.code)
+ .collection(VENDORS)
+ .get()
+ .addOnSuccessListener {
+ val vendors = it.toObjects(FirebaseVendor::class.java)
+ .filter { !it.hidden || App.isDeveloper }
+ .map { it.toVendor() }
+
+ mutableLiveData.postValue(vendors)
+ }
+ return mutableLiveData
+ }
+
+ suspend fun getEventById(conference: String, id: Int): Event? {
+ val snapshot = firestore.collection(CONFERENCES)
+ .document(conference)
+ .collection(EVENTS)
+ .document(id.toString())
+ .get()
+ .await()
+
+ return snapshot.toObject(FirebaseEvent::class.java)?.toEvent()
+ }
+
+ fun updateBookmark(event: Event) {
+ val tag = "reminder_" + event.id
+
+ if (event.isBookmarked) {
+ val delay = event.start.time - MyClock().now().time - (1000 * 20 * 60)
+
+ if (delay > 0) {
+ val data = workDataOf(
+ ReminderWorker.INPUT_ID to event.id,
+ ReminderWorker.INPUT_CONFERENCE to event.conference
+ )
+
+ val notify = OneTimeWorkRequestBuilder()
+ .setInputData(data)
+ .setInitialDelay(delay, TimeUnit.MILLISECONDS)
+ .addTag(tag)
+ .build()
+
+ WorkManager.getInstance().enqueue(notify)
+ }
+
+ } else {
+ WorkManager.getInstance().cancelAllWorkByTag(tag)
+ }
+
+ val id = user?.uid ?: return
+
+ val document = firestore.collection(CONFERENCES)
+ .document(event.conference)
+ .collection(USERS)
+ .document(id)
+ .collection(BOOKMARKS)
+ .document(event.id.toString())
+
+ if (event.isBookmarked) {
+ document.set(
+ mapOf(
+ "id" to event.id.toString(),
+ "value" to true
+ )
+ )
+ } else {
+ document.delete()
+ }
+ }
+
+ fun updateTypeIsSelected(type: Type) {
+ val id = user?.uid ?: return
+
+ val document = firestore.collection(CONFERENCES)
+ .document(type.conference)
+ .collection(USERS)
+ .document(id)
+ .collection(TYPES)
+ .document(type.id.toString())
+
+ if (type.isSelected) {
+ document.set(
+ mapOf(
+ "id" to type.id.toString(),
+ "value" to true
+ )
+ )
+ } else {
+ document.delete()
+ }
+ }
+
+ private fun updateFirebaseMessagingToken(conference: Conference?, token: String?) {
+ val id = user?.uid
+
+ if (conference == null || token == null || id == null) {
+ Log.e("TAG", "Null, cannot update token.")
+ return
+ }
+
+ val document = firestore.collection(CONFERENCES)
+ .document(conference.code)
+ .collection(USERS)
+ .document(id)
+
+
+ document.set(mapOf("token" to token))
+ }
+
+ fun clear() {
+
+ }
+
+
+ fun getSpeakers(conference: Conference): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(conference.code)
+ .collection(SPEAKERS)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val speakers = snapshot?.toObjects(FirebaseSpeaker::class.java)
+ ?.filter { !it.hidden || App.isDeveloper }
+ ?.map { it.toSpeaker() }
+ ?: emptyList()
+
+ mutableLiveData.postValue(speakers)
+ }
+ }
+
+ return mutableLiveData
+ }
+
+ fun getEventsForSpeaker(speaker: Speaker): LiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ firestore.collection(CONFERENCES)
+ .document(code)
+ .collection(EVENTS)
+ .addSnapshotListener { snapshot, exception ->
+ if (exception == null) {
+ val events = snapshot?.toObjects(FirebaseEvent::class.java)
+ val filtered =
+ events?.filter { it.speakers.firstOrNull { it.id == speaker.id } != null }
+ ?.map { it.toEvent() }
+ mutableLiveData.postValue(filtered)
+ }
+ }
+
+ return mutableLiveData
+ }
+
+
+ fun getMaps(conference: Conference): MutableLiveData> {
+ val mutableLiveData = MutableLiveData>()
+
+ val list = ArrayList()
+
+ val maps = conference.maps
+ if (maps.isEmpty()) {
+ return mutableLiveData
+ }
+
+ maps.forEach {
+ val map = FirebaseConferenceMap(it.name, it.file, null)
+ list.add(map)
+ }
+
+ mutableLiveData.postValue(list)
+
+ list.forEach {
+ val filename = "${conference.code}-${it.path}"
+ val file = File(App.instance.applicationContext.getExternalFilesDir(null), filename)
+ if (file.exists()) {
+ it.file = file
+ } else {
+ file.createNewFile()
+
+ val map = storage.reference.child("/${conference.code}/${it.path}")
+
+ map.getFile(file).addOnSuccessListener { task ->
+ it.file = file
+
+ mutableLiveData.postValue(list.toList())
+ }.addOnFailureListener {
+ // Handle any errors
+ }
+ }
+ }
+ return mutableLiveData
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/database/ReminderManager.kt b/app/src/main/java/com/shortstack/hackertracker/database/ReminderManager.kt
new file mode 100644
index 00000000..641d041d
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/database/ReminderManager.kt
@@ -0,0 +1,53 @@
+package com.shortstack.hackertracker.database
+
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.workDataOf
+import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.network.task.ReminderWorker
+import com.shortstack.hackertracker.utilities.MyClock
+import com.shortstack.hackertracker.utilities.now
+import java.util.concurrent.TimeUnit
+
+class ReminderManager(
+ private val databaseManager: DatabaseManager,
+ private val workManager: WorkManager
+) {
+
+ companion object {
+ private const val TWENTY_MINUTES_BEFORE = 1000 * 20 * 60
+ private const val TAG = "reminder_"
+ }
+
+ suspend fun getEvent(conference: String, id: Int): Event? {
+ return databaseManager.getEventById(conference, id)
+ }
+
+ fun setReminder(event: Event) {
+ val start = event.start
+ val now = MyClock().now()
+
+ val delay = start.time - now.time - TWENTY_MINUTES_BEFORE
+
+ if (delay < 0) {
+ return
+ }
+
+ val data = workDataOf(
+ ReminderWorker.INPUT_ID to event.id,
+ ReminderWorker.INPUT_CONFERENCE to event.conference
+ )
+
+ val notify = OneTimeWorkRequestBuilder()
+ .setInputData(data)
+ .setInitialDelay(delay, TimeUnit.MILLISECONDS)
+ .addTag(TAG + event.id)
+ .build()
+
+ workManager.enqueue(notify)
+ }
+
+ fun cancel(event: Event) {
+ workManager.cancelAllWorkByTag(TAG + event.id)
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/di/modules.kt b/app/src/main/java/com/shortstack/hackertracker/di/modules.kt
similarity index 56%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/di/modules.kt
rename to app/src/main/java/com/shortstack/hackertracker/di/modules.kt
index e6d6ec78..6428c4a0 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/di/modules.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/di/modules.kt
@@ -1,25 +1,31 @@
package com.shortstack.hackertracker.di
+import androidx.work.WorkManager
import com.firebase.jobdispatcher.FirebaseJobDispatcher
import com.firebase.jobdispatcher.GooglePlayDriver
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
-import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.database.ReminderManager
+import com.shortstack.hackertracker.ui.themes.ThemesManager
+import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.utilities.NotificationHelper
import com.shortstack.hackertracker.utilities.Storage
-import com.shortstack.hackertracker.utilities.TickTimer
-import org.koin.dsl.module.module
+import org.koin.dsl.module
val appModule = module {
- single { TickTimer() }
- single { Storage(get()) }
+ single { Storage(get(), get()) }
single { NotificationHelper(get()) }
- single { GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create() }
+ single {
+ GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+ }
single { FirebaseJobDispatcher(GooglePlayDriver(get())) }
single { DatabaseManager(get()) }
+ single { ThemesManager() }
- single { Analytics(get()) }
+ single { Analytics(get(), get()) }
+ single { WorkManager.getInstance()!! }
+ single { ReminderManager(get(), get()) }
}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/Day.kt b/app/src/main/java/com/shortstack/hackertracker/models/Day.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/Day.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/Day.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/Information.kt b/app/src/main/java/com/shortstack/hackertracker/models/Information.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/Information.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/Information.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/Time.kt b/app/src/main/java/com/shortstack/hackertracker/models/Time.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/Time.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/Time.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt
similarity index 87%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt
index 073bb489..efe4d470 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseArticle.kt
@@ -1,6 +1,7 @@
package com.shortstack.hackertracker.models.firebase
data class FirebaseArticle(
+ val id: Int = -1,
val name: String = "",
val text: String = "",
val hidden: Boolean = false
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseBookmark.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseBookmark.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseBookmark.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseBookmark.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt
similarity index 89%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt
index 1a1d0bf1..3248f2d9 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConference.kt
@@ -11,11 +11,13 @@ data class FirebaseConference(
val id: Int = 0,
val name: String = "",
val description: String = "",
+ val codeofconduct: String? = null,
val code: String = "",
val maps: ArrayList = ArrayList(),
val start_date: String = "",
val end_date: String = "",
val start_timestamp: Timestamp = Timestamp(Date()),
val end_timestamp: Timestamp = Timestamp(Date()),
+ val timezone: String = "",
val hidden: Boolean = false
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt
new file mode 100644
index 00000000..7ef332ee
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt
@@ -0,0 +1,5 @@
+package com.shortstack.hackertracker.models.firebase
+
+import java.io.File
+
+data class FirebaseConferenceMap(val title: String, val path: String, var file: File?)
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseEvent.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseEvent.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseEvent.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseEvent.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseFAQ.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseFAQ.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseFAQ.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseFAQ.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt
similarity index 87%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt
index 75144395..eaa5f76d 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseLocation.kt
@@ -6,5 +6,6 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
data class FirebaseLocation(
val name: String = "",
+ val hotel: String? = null,
val conference: String = ""
) : Parcelable
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseMap.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseMap.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseMap.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseMap.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseSpeaker.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseSpeaker.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseSpeaker.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseSpeaker.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt
similarity index 69%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt
index bae2df1d..79537149 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseType.kt
@@ -9,9 +9,4 @@ data class FirebaseType(
val name: String = "",
val conference: String = "",
val color: String = "#343434"
-) : Parcelable {
-
- val filtered: Boolean
- get() = name.contains("Workshop", true) || name.contains("Contest", true)
-
-}
\ No newline at end of file
+) : Parcelable
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseVendor.kt b/app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseVendor.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseVendor.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseVendor.kt
diff --git a/app/src/main/java/com/shortstack/hackertracker/models/local/Article.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Article.kt
new file mode 100644
index 00000000..e5a3634a
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Article.kt
@@ -0,0 +1,3 @@
+package com.shortstack.hackertracker.models.local
+
+data class Article(val id: Int, val name: String, val text: String)
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt
similarity index 92%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt
index 21cec436..6a53bcdb 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Conference.kt
@@ -13,10 +13,12 @@ data class Conference(
val id: Int,
val name: String,
val description: String,
+ val conduct: String?,
val code: String,
val maps: ArrayList,
val startDate: Date,
val endDate: Date,
+ val timezone: String,
var isSelected: Boolean = false
) : Parcelable {
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Event.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Event.kt
similarity index 80%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Event.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Event.kt
index d0f6077a..2d2da696 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Event.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Event.kt
@@ -7,6 +7,7 @@ import com.shortstack.hackertracker.utilities.MyClock
import com.shortstack.hackertracker.utilities.TimeUtil
import com.shortstack.hackertracker.utilities.now
import kotlinx.android.parcel.Parcelize
+import java.text.SimpleDateFormat
import java.util.*
@Parcelize
@@ -22,7 +23,8 @@ data class Event(
val speakers: List,
val type: Type,
val location: Location,
- var isBookmarked: Boolean = false) : Parcelable {
+ var isBookmarked: Boolean = false,
+ var key: Long = -1) : Parcelable {
val progress: Float
get() {
@@ -61,15 +63,14 @@ data class Event(
}
fun getFullTimeStamp(context: Context): String {
- val (begin, end) = getTimeStamp(context)
- val timestamp = TimeUtil.getRelativeDateStamp(context, start)
+ val date = TimeUtil.getDateStamp(start)
- return String.format(context.getString(R.string.timestamp_full), timestamp, begin, end)
- }
+ val time = if (android.text.format.DateFormat.is24HourFormat(context)) {
+ SimpleDateFormat("HH:mm").format(start)
+ } else {
+ SimpleDateFormat("h:mm aa").format(start)
+ }
- private fun getTimeStamp(context: Context): Pair {
- val begin = TimeUtil.getTimeStamp(context, start)
- val end = TimeUtil.getTimeStamp(context, end)
- return Pair(begin, end)
+ return String.format(context.getString(R.string.timestamp_start), date, time)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/models/local/FAQ.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/FAQ.kt
new file mode 100644
index 00000000..bb8b0447
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/FAQ.kt
@@ -0,0 +1,8 @@
+package com.shortstack.hackertracker.models.local
+
+data class FAQ(
+ val id: Int,
+ val question: String,
+ val answer: String,
+ var isExpanded: Boolean = false
+)
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Location.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Location.kt
similarity index 88%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Location.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Location.kt
index 5acd65c9..f713b1ed 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Location.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Location.kt
@@ -6,5 +6,6 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
data class Location(
val name: String,
+ val hotel: String?,
val conference: String
) : Parcelable
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Speaker.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Speaker.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Speaker.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Speaker.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Type.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Type.kt
similarity index 78%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Type.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Type.kt
index d7222293..2214bf50 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Type.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Type.kt
@@ -14,6 +14,4 @@ data class Type(
val isBookmark: Boolean
get() = name.contains("bookmark", true)
- val filtered: Boolean
- get() = name.contains("Workshop", true) || name.contains("Contest", true)
}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt b/app/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt
similarity index 75%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt
rename to app/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt
index 6de399ab..3755dc56 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/models/local/Vendor.kt
@@ -12,18 +12,11 @@ data class Vendor(
val partner: Boolean
) : Parcelable {
- val colour: Int
- get() {
- if (id == -1)
- return 0
- return id
- }
-
val summary: String
get() {
if (description.isNullOrBlank())
return "Nothing to say."
- return description
+ return description.replace("\\n", "\n")
}
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt b/app/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt
new file mode 100644
index 00000000..cc8b0352
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt
@@ -0,0 +1,40 @@
+package com.shortstack.hackertracker.network.task
+
+import android.content.Context
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.utilities.NotificationHelper
+import kotlinx.coroutines.runBlocking
+import org.koin.core.KoinComponent
+import org.koin.core.inject
+
+class ReminderWorker(context: Context, params: WorkerParameters) : Worker(context, params),
+ KoinComponent {
+
+ private val database: DatabaseManager by inject()
+ private val notifications: NotificationHelper by inject()
+
+ override fun doWork(): Result {
+ val conference = inputData.getString(INPUT_CONFERENCE)
+ val id = inputData.getInt(INPUT_ID, -1)
+
+ if (conference == null || id == -1) {
+ return Result.failure()
+ }
+
+ runBlocking {
+ val event = database.getEventById(conference, id)
+ if (event != null) {
+ notifications.notifyStartingSoon(event)
+ }
+ }
+
+ return Result.success()
+ }
+
+ companion object {
+ const val INPUT_CONFERENCE = "INPUT_CONFERENCE"
+ const val INPUT_ID = "INPUT_ID"
+ }
+}
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/HackerTrackerViewModel.kt b/app/src/main/java/com/shortstack/hackertracker/ui/HackerTrackerViewModel.kt
new file mode 100644
index 00000000..bfc00bc0
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/HackerTrackerViewModel.kt
@@ -0,0 +1,337 @@
+package com.shortstack.hackertracker.ui
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.shortstack.hackertracker.Resource
+import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.models.firebase.FirebaseConferenceMap
+import com.shortstack.hackertracker.models.local.*
+import com.shortstack.hackertracker.ui.themes.ThemesManager
+import com.shortstack.hackertracker.utilities.Storage
+import org.koin.core.KoinComponent
+import org.koin.core.inject
+
+class HackerTrackerViewModel : ViewModel(), KoinComponent {
+
+ private val database: DatabaseManager by inject()
+ private val storage: Storage by inject()
+ private val themes: ThemesManager by inject()
+
+
+ val conference: LiveData>
+ val events: LiveData>>
+ val bookmarks: LiveData>>
+ val types: LiveData>>
+ val locations: LiveData>>
+ val speakers: LiveData>>
+
+ val articles: LiveData>>
+ val faq: LiveData>>
+ val vendors: LiveData>>
+
+ val maps: LiveData>>
+
+ // Home
+ val home: LiveData>>
+
+ // Schedule
+ val schedule: LiveData>>
+
+
+ // Search
+ private val query = MediatorLiveData()
+ val search: LiveData>
+
+
+ init {
+ conference = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.value = Resource.success(it)
+ }
+
+
+ return@switchMap result
+ }
+
+ types = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getTypes(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+ return@switchMap result
+ }
+
+ locations = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getLocations(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+ return@switchMap result
+ }
+
+ events = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getSchedule()) {
+ result.value = Resource.success(it)
+ }
+ }
+
+
+ return@switchMap result
+ }
+
+ schedule = Transformations.switchMap(database.conference) { id ->
+ val result = MediatorLiveData>>()
+
+ if (id == null) {
+ result.value = Resource.init(null)
+ return@switchMap result
+ }
+
+ result.value = Resource.loading(null)
+
+ result.addSource(events) {
+ val types = types.value?.data ?: emptyList()
+ result.value = Resource.success(getSchedule(it?.data ?: emptyList(), types))
+ }
+
+ result.addSource(types) { types ->
+ val events = events.value?.data ?: return@addSource
+ result.value = Resource.success(getSchedule(events, types?.data ?: emptyList()))
+ }
+
+ return@switchMap result
+ }
+
+ bookmarks = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init(null)
+ return@switchMap result
+ } else {
+ result.addSource(database.getBookmarks(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+
+
+ return@switchMap result
+ }
+
+
+ speakers = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getSpeakers(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+
+ return@switchMap result
+ }
+
+ articles = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getArticles(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+
+ return@switchMap result
+ }
+
+ faq = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getFAQ(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+ return@switchMap result
+ }
+
+ vendors = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getVendors(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+ return@switchMap result
+ }
+
+ maps = Transformations.switchMap(database.conference) {
+ val result = MediatorLiveData>>()
+
+ if (it == null) {
+ result.value = Resource.init()
+ } else {
+ result.addSource(database.getMaps(it)) {
+ result.value = Resource.success(it)
+ }
+ }
+
+ return@switchMap result
+ }
+
+ search = Transformations.switchMap(query) { text ->
+ val results = MediatorLiveData>()
+
+ results.addSource(events) {
+ val locations = locations.value?.data ?: emptyList()
+ val speakers = speakers.value?.data ?: emptyList()
+ setValue(results, text, it?.data ?: emptyList(), locations, speakers)
+ }
+
+ results.addSource(locations) {
+ val events = events.value?.data ?: emptyList()
+ val speakers = speakers.value?.data ?: emptyList()
+ setValue(results, text, events, it?.data ?: emptyList(), speakers)
+ }
+
+ results.addSource(speakers) {
+ val events = events.value?.data ?: emptyList()
+ val locations = locations.value?.data ?: emptyList()
+ setValue(results, text, events, locations, it?.data ?: emptyList())
+ }
+
+ return@switchMap results
+ }
+
+ home = Transformations.switchMap(database.conference) { id ->
+ val result = MediatorLiveData>>()
+
+ if (id == null) {
+ result.value = Resource.init(null)
+ return@switchMap result
+ }
+
+ result.value = Resource.loading(null)
+
+ result.addSource(bookmarks) {
+ val articles = articles.value?.data?.take(4) ?: emptyList()
+ result.value = Resource.success(articles + (it.data?.take(3) ?: emptyList()))
+ }
+
+ result.addSource(articles) {
+ val bookmarks = bookmarks.value?.data?.take(3) ?: emptyList()
+ result.value = Resource.success((it.data?.take(4) ?: emptyList()) + bookmarks)
+ }
+
+ return@switchMap result
+ }
+
+ }
+
+ private fun getSchedule(events: List, types: List): List {
+ if (types.isEmpty())
+ return events
+
+ val requireBookmark = types.firstOrNull { it.isBookmark }?.isSelected ?: false
+ val filter = types.filter { !it.isBookmark && it.isSelected }
+ if (!requireBookmark && filter.isEmpty())
+ return events
+
+ if (requireBookmark && filter.isEmpty())
+ return events.filter { it.isBookmarked }
+
+ return events.filter { event -> isShown(event, requireBookmark, filter) }
+ }
+
+ private fun isShown(event: Event, requireBookmark: Boolean, filter: List): Boolean {
+ val bookmark = if (requireBookmark) {
+ event.isBookmarked
+ } else {
+ true
+ }
+
+ return bookmark && filter.find { it.id == event.type.id }?.isSelected == true
+ }
+
+ private fun setValue(
+ results: MediatorLiveData>,
+ query: String,
+ events: List,
+ locations: List,
+ speakers: List
+ ) {
+ if (query.isBlank()) {
+ results.value = emptyList()
+ return
+ }
+
+ val list = ArrayList()
+
+ val speakers = speakers.filter {
+ it.name.contains(query, true) || it.description.contains(
+ query,
+ true
+ )
+ }
+ if (speakers.isNotEmpty()) {
+ list.add("Speakers")
+ list.addAll(speakers)
+ }
+
+ val locations = locations.filter { it.name.contains(query, true) }
+ locations.forEach { location ->
+ list.add(location)
+ // TODO: Should we add the filtered events, or all events for this location?
+ list.addAll(events.filter { it.location.name == location.name }.sortedBy { it.start })
+ }
+
+ val events =
+ events.filter { it.title.contains(query, true) || it.description.contains(query, true) }
+ if (events.isNotEmpty()) {
+ list.add("Events")
+ list.addAll(events)
+ }
+
+ results.value = list
+ }
+
+
+ fun onQueryTextChange(text: String?) {
+ query.value = text
+ }
+
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt
similarity index 72%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt
index 34ef8184..b8fffa96 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/ListAdapter.kt
@@ -3,19 +3,14 @@ package com.shortstack.hackertracker.ui
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.models.Day
-import com.shortstack.hackertracker.models.Time
-import com.shortstack.hackertracker.models.firebase.FirebaseFAQ
-import com.shortstack.hackertracker.models.local.Location
-import com.shortstack.hackertracker.models.local.Speaker
-import com.shortstack.hackertracker.models.local.Event
-import com.shortstack.hackertracker.models.local.Vendor
-import com.shortstack.hackertracker.ui.information.FAQViewHolder
+import com.shortstack.hackertracker.models.local.*
+import com.shortstack.hackertracker.ui.information.faq.FAQViewHolder
+import com.shortstack.hackertracker.ui.information.speakers.SpeakerViewHolder
+import com.shortstack.hackertracker.ui.information.vendors.VendorViewHolder
import com.shortstack.hackertracker.ui.schedule.DayViewHolder
import com.shortstack.hackertracker.ui.schedule.EventViewHolder
-import com.shortstack.hackertracker.ui.schedule.TimeViewHolder
import com.shortstack.hackertracker.ui.search.LocationViewHolder
-import com.shortstack.hackertracker.ui.speakers.SpeakerViewHolder
-import com.shortstack.hackertracker.ui.vendors.VendorViewHolder
+import com.shortstack.hackertracker.views.EventView
class ListAdapter : RecyclerView.Adapter() {
@@ -24,7 +19,6 @@ class ListAdapter : RecyclerView.Adapter() {
private const val LOCATION = 1
private const val SPEAKER = 2
private const val DAY = 3
- private const val TIME = 4
private const val VENDOR = 5
private const val FAQ = 6
}
@@ -33,11 +27,10 @@ class ListAdapter : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
- EVENT -> EventViewHolder.inflate(parent)
+ EVENT -> EventViewHolder.inflate(parent, EventView.DISPLAY_MODE_MIN)
SPEAKER -> SpeakerViewHolder.inflate(parent)
LOCATION -> LocationViewHolder.inflate(parent)
DAY -> DayViewHolder.inflate(parent)
- TIME -> TimeViewHolder.inflate(parent)
VENDOR -> VendorViewHolder.inflate(parent)
FAQ -> FAQViewHolder.inflate(parent)
else -> throw IllegalStateException("Unknown viewType $viewType.")
@@ -54,9 +47,8 @@ class ListAdapter : RecyclerView.Adapter() {
is SpeakerViewHolder -> holder.render(item as Speaker)
is LocationViewHolder -> holder.render(item as Location)
is DayViewHolder -> holder.render(item as Day)
- is TimeViewHolder -> holder.render(item as Time)
is VendorViewHolder -> holder.render(item as Vendor)
- is FAQViewHolder -> holder.render(item as FirebaseFAQ)
+ is FAQViewHolder -> holder.render(item as FAQ)
}
}
@@ -66,9 +58,8 @@ class ListAdapter : RecyclerView.Adapter() {
is Location -> LOCATION
is Event -> EVENT
is Day -> DAY
- is Time -> TIME
is Vendor -> VENDOR
- is FirebaseFAQ -> FAQ
+ is FAQ -> FAQ
else -> throw java.lang.IllegalStateException("Unknown viewType ${collection[position].javaClass}")
}
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/ListFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/ListFragment.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/ListFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/ListFragment.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt b/app/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt
similarity index 54%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt
index 00ab4e7c..af7b7f9a 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivity.kt
@@ -1,126 +1,131 @@
package com.shortstack.hackertracker.ui.activities
+import android.content.Context
+import android.content.Intent
import android.content.res.Resources
+import android.os.Build
import android.os.Bundle
+import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.view.View
-import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
-import androidx.core.view.ViewCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.github.stkent.amplify.tracking.Amplify
-import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener
import com.google.firebase.auth.FirebaseAuth
import com.orhanobut.logger.Logger
import com.shortstack.hackertracker.BuildConfig
import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.local.Speaker
import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.models.local.Location
+import com.shortstack.hackertracker.models.local.Speaker
+import com.shortstack.hackertracker.models.local.Type
import com.shortstack.hackertracker.replaceFragment
-import com.shortstack.hackertracker.ui.SearchFragment
-import com.shortstack.hackertracker.ui.settings.SettingsFragment
-import com.shortstack.hackertracker.ui.contests.ContestsFragment
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.search.SearchFragment
import com.shortstack.hackertracker.ui.events.EventFragment
import com.shortstack.hackertracker.ui.home.HomeFragment
import com.shortstack.hackertracker.ui.information.InformationFragment
+import com.shortstack.hackertracker.ui.information.speakers.SpeakerFragment
import com.shortstack.hackertracker.ui.maps.MapsFragment
import com.shortstack.hackertracker.ui.schedule.ScheduleFragment
-import com.shortstack.hackertracker.ui.speakers.SpeakerFragment
-import com.shortstack.hackertracker.ui.speakers.SpeakersFragment
-import com.shortstack.hackertracker.ui.vendors.VendorsFragment
-import com.shortstack.hackertracker.ui.workshops.WorkshopFragment
-import com.shortstack.hackertracker.utilities.TickTimer
+import com.shortstack.hackertracker.ui.settings.SettingsFragment
+import com.shortstack.hackertracker.ui.themes.ThemesManager.Theme.*
+import com.shortstack.hackertracker.utilities.Storage
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.nav_header_main.view.*
import kotlinx.android.synthetic.main.row_nav_view.*
-import kotlinx.android.synthetic.main.view_filter.*
import org.koin.android.ext.android.inject
class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, FragmentManager.OnBackStackChangedListener {
- private val database: DatabaseManager by inject()
-
- private val timer: TickTimer by inject()
+ private val storage: Storage by inject()
- private lateinit var bottomSheet: BottomSheetBehavior
-
- private lateinit var viewModel: MainActivityViewModel
+ private lateinit var viewModel: HackerTrackerViewModel
private val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance() }
private val map = HashMap()
+ private var secondaryVisible = false
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
setContentView(R.layout.activity_main)
- setSupportActionBar(toolbar)
initNavDrawer()
- viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
+ viewModel = ViewModelProvider(this)[HackerTrackerViewModel::class.java]
viewModel.conference.observe(this, Observer {
if (it != null) {
- nav_view.getHeaderView(0).nav_title.text = it.name
- }
- })
-
- viewModel.types.observe(this, Observer {
-
- val hasContest = it.firstOrNull { it.name == "Contest" } != null
- if (!hasContest) {
- nav_view.menu.removeItem(NAV_CONTESTS)
- } else if (nav_view.menu.findItem(NAV_CONTESTS) == null) {
- nav_view.menu.add(R.id.nav_main, NAV_CONTESTS, 3, R.string.contests).apply {
- icon = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_cake_white_24dp)
- }
- }
-
- val hasWorkshops = it.firstOrNull { it.name == "Workshop" } != null
- if (!hasWorkshops) {
- nav_view.menu.removeItem(NAV_WORKSHOPS)
- } else if (nav_view.menu.findItem(NAV_WORKSHOPS) == null) {
- nav_view.menu.add(R.id.nav_main, NAV_WORKSHOPS, 3, R.string.workshops).apply {
- icon = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_computer_white_24dp)
- }
+ nav_view.getHeaderView(0).nav_title.text = it.data?.name
}
-
- filters.setTypes(it)
})
- bottomSheet = BottomSheetBehavior.from(filters)
- bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
-
- filter.setOnClickListener { expandFilters() }
- close.setOnClickListener { hideFilters() }
-
if (savedInstanceState == null) {
if (Amplify.getSharedInstance().shouldPrompt() && !BuildConfig.DEBUG) {
val review = ReviewBottomSheet.newInstance()
review.show(this.supportFragmentManager, review.tag)
}
+
+
+ setMainFragment(R.id.nav_home, getString(R.string.home), false)
}
supportFragmentManager.addOnBackStackChangedListener(this)
+ }
- setMainFragment(R.id.nav_schedule, getString(R.string.schedule), false)
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
- ViewCompat.setTranslationZ(filters, 10f)
+ val target = intent?.getIntExtra("target", -1)
+ if (target != null && target != -1) {
+ navigate(target)
+ }
}
+ override fun onResume() {
+ super.onResume()
+
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val value = TypedValue()
+ theme.resolveAttribute(R.attr.dark_mode, value, true)
+ if (value.string == "dark") {
+ window.decorView.systemUiVisibility = 0
+ window.statusBarColor = getThemeAccentColor(this)
+ } else {
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+ window.statusBarColor = getThemeAccentColor(this)
+ }
+ }
+ }
+
+ private fun getThemeAccentColor(context: Context, theme: Resources.Theme = context.theme): Int {
+ val colorAttr = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ android.R.attr.colorBackground
+ } else {
+ context.resources.getIdentifier("colorBackground", "attr", context.packageName)
+ }
+ val outValue = TypedValue()
+ theme.resolveAttribute(colorAttr, outValue, true)
+ return outValue.data
+ }
+
+
+
+
override fun onStart() {
super.onStart()
auth.signInAnonymously().addOnCompleteListener(this) {
@@ -133,51 +138,25 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
}
}
- override fun onResume() {
- super.onResume()
- timer.start()
- }
-
- override fun onPause() {
- timer.stop()
- super.onPause()
- }
-
- private lateinit var toggle: ActionBarDrawerToggle
-
private fun initNavDrawer() {
- toggle = ActionBarDrawerToggle(
- this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
- drawer_layout.addDrawerListener(toggle)
- toggle.syncState()
-
nav_view.setNavigationItemSelectedListener(this)
}
override fun getTheme(): Resources.Theme {
val theme = super.getTheme()
- theme.applyStyle(R.style.AppTheme, true)
- return theme
- }
- private fun setFABVisibility(visibility: Int) {
- if (visibility == View.VISIBLE) {
- filter.show()
- } else {
- filter.hide()
+ val style = when (storage.theme) {
+ Dark -> R.style.AppTheme_Dark
+ Light -> R.style.AppTheme
+ Developer -> R.style.AppTheme_Developer
+ null -> R.style.AppTheme_Dark
}
- }
+ theme.applyStyle(style, true)
- private fun expandFilters() {
- bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
- }
-
- private fun hideFilters() {
- bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
+ return theme
}
-
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.search_menu, menu)
@@ -185,9 +164,11 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
}
override fun onBackPressed() {
+ val drawerOpen = drawer_layout.isDrawerOpen(GravityCompat.START)
+
when {
- drawer_layout.isDrawerOpen(GravityCompat.START) -> drawer_layout.closeDrawers()
- bottomSheet.state != BottomSheetBehavior.STATE_HIDDEN -> hideFilters()
+ drawerOpen -> drawer_layout.closeDrawers()
+ storage.navDrawerOnBack && !drawerOpen && !secondaryVisible -> drawer_layout.openDrawer(GravityCompat.START)
else -> super.onBackPressed()
}
}
@@ -202,9 +183,6 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
}
private fun setMainFragment(id: Int, title: String? = null, addToBackStack: Boolean) {
- val visibility = if (id == R.id.nav_schedule) View.VISIBLE else View.INVISIBLE
- setFABVisibility(visibility)
-
replaceFragment(getFragment(id), R.id.container, backStack = addToBackStack)
title?.let {
@@ -214,26 +192,24 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
- if (item.groupId == R.id.nav_cons) {
- viewModel.changeConference(item.itemId)
- } else {
- setMainFragment(item.itemId, item.title.toString(), false)
- }
-
+ setMainFragment(item.itemId, item.title.toString(), false)
drawer_layout.closeDrawers()
return true
}
private fun getFragment(id: Int): Fragment {
+ // TODO: Remove, this is a hacky solution for caching issue with InformationFragment's children fragments.
+ if (id == R.id.nav_information)
+ return InformationFragment.newInstance()
+
+ if (id == R.id.nav_map)
+ return MapsFragment.newInstance()
+
if (map[id] == null) {
map[id] = when (id) {
R.id.nav_home -> HomeFragment.newInstance()
R.id.nav_schedule -> ScheduleFragment.newInstance()
R.id.nav_map -> MapsFragment.newInstance()
- R.id.nav_speakers -> SpeakersFragment.newInstance()
- R.id.nav_companies -> VendorsFragment.newInstance()
- NAV_CONTESTS -> ContestsFragment.newInstance()
- NAV_WORKSHOPS -> WorkshopFragment.newInstance()
R.id.nav_settings -> SettingsFragment.newInstance()
R.id.search -> SearchFragment.newInstance()
else -> InformationFragment.newInstance()
@@ -243,7 +219,11 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
}
fun navigate(event: Event) {
- replaceFragment(EventFragment.newInstance(event), R.id.container_above, hasAnimation = true)
+ navigate(event.id)
+ }
+
+ fun navigate(id: Int) {
+ replaceFragment(EventFragment.newInstance(id), R.id.container_above, hasAnimation = true)
}
fun navigate(speaker: Speaker?) {
@@ -260,24 +240,30 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, Frag
val last = fragments.lastOrNull()
if (last is EventFragment || last is SpeakerFragment) {
+ secondaryVisible = true
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- toolbar.visibility = View.INVISIBLE
container.visibility = View.INVISIBLE
- setFABVisibility(View.INVISIBLE)
} else {
+ secondaryVisible = false
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
- toolbar.visibility = View.VISIBLE
container.visibility = View.VISIBLE
- if (last is ScheduleFragment) {
- setFABVisibility(View.VISIBLE)
- } else {
- setFABVisibility(View.INVISIBLE)
- }
}
}
- companion object {
- private const val NAV_WORKSHOPS = 1001
- private const val NAV_CONTESTS = 1002
+ fun openNavDrawer() {
+ drawer_layout.openDrawer(GravityCompat.START)
+ }
+
+ fun showSearch() {
+ setMainFragment(R.id.search, getString(R.string.search), true)
+ }
+
+ fun showMap(location: Location) {
+ supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
+
+ val fragment = MapsFragment.newInstance(location)
+ map[R.id.nav_map] = fragment
+
+ setMainFragment(R.id.nav_map, getString(R.string.map), true)
}
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/ReviewBottomSheet.kt b/app/src/main/java/com/shortstack/hackertracker/ui/activities/ReviewBottomSheet.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/ReviewBottomSheet.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/activities/ReviewBottomSheet.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt
similarity index 58%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt
index 9cf464a1..c1aed0c2 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/events/EventFragment.kt
@@ -6,21 +6,24 @@ import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
+import android.util.TypedValue
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.local.Speaker
+import com.shortstack.hackertracker.database.ReminderManager
import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.utilities.TimeUtil
import com.shortstack.hackertracker.views.SpeakerView
-import com.shortstack.hackertracker.views.StatusBarSpacer
import kotlinx.android.synthetic.main.empty_text.*
import kotlinx.android.synthetic.main.fragment_event.*
import org.koin.android.ext.android.inject
@@ -31,34 +34,43 @@ class EventFragment : Fragment() {
const val EXTRA_EVENT = "EXTRA_EVENT"
- fun newInstance(event: Event): EventFragment {
+ fun newInstance(event: Int): EventFragment {
val fragment = EventFragment()
val bundle = Bundle()
- bundle.putParcelable(EXTRA_EVENT, event)
+ bundle.putInt(EXTRA_EVENT, event)
fragment.arguments = bundle
return fragment
}
}
- private val database: DatabaseManager by inject()
private val analytics: Analytics by inject()
+ private val database: DatabaseManager by inject()
+ private val reminder: ReminderManager by inject()
+ private val viewModel: HackerTrackerViewModel by lazy { ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java] }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_event, container, false)
}
- override fun onPrepareOptionsMenu(menu: Menu?) {
- menu?.clear()
+ override fun onPrepareOptionsMenu(menu: Menu) {
+ menu.clear()
super.onPrepareOptionsMenu(menu)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
- val event = arguments?.getParcelable(EXTRA_EVENT) as? Event
+ val id = arguments?.getInt(EXTRA_EVENT)
+
+ viewModel.events.observe(this, Observer {
+ val target = it.data?.find { it.id == id }
+ if (target != null) {
+ showEvent(target)
+ }
+ })
val drawable = ContextCompat.getDrawable(context
@@ -69,60 +81,71 @@ class EventFragment : Fragment() {
(activity as? MainActivity)?.popBackStack()
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- val context = context ?: return
- val height = StatusBarSpacer.getStatusBarHeight(context, app_bar)
- app_bar.setPadding(0, height, 0, 0)
- }
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+// val context = context ?: return
+// val height = StatusBarSpacer.getStatusBarHeight(context, app_bar)
+// app_bar.setPadding(0, height, 0, 0)
+// }
+ }
+
+ private fun showEvent(event: Event) {
+ analytics.log("Viewing event ${event.title}")
- event?.let { event ->
+ collapsing_toolbar.title = event.title
- analytics.log("Viewing event ${event.title}")
+ val body = event.description
- collapsing_toolbar.title = event.title
+ if (body.isNotBlank()) {
+ empty.visibility = View.GONE
+ description.text = body
+ } else {
+ empty.visibility = View.VISIBLE
+ }
- val body = event.description
+ val url = event.link
+ if (url.isBlank()) {
+ link.visibility = View.GONE
+ } else {
+ link.visibility = View.VISIBLE
- if (body.isNotBlank()) {
- empty.visibility = View.GONE
- description.text = body
- } else {
- empty.visibility = View.VISIBLE
+ link.setOnClickListener {
+ onLinkClick(url)
+ analytics.onEventAction(Analytics.EVENT_OPEN_URL, event)
}
+ }
- val url = event.link
- if (url.isBlank()) {
- link.visibility = View.GONE
- } else {
- link.visibility = View.VISIBLE
+ share.setOnClickListener {
+ onShareClick(event)
+ analytics.onEventAction(Analytics.EVENT_SHARE, event)
+ }
- link.setOnClickListener {
- onLinkClick(url)
- analytics.onEventAction(Analytics.EVENT_OPEN_URL, event)
- }
- }
+ star.setOnClickListener {
+ onBookmarkClick(event)
+ }
- share.setOnClickListener {
- onShareClick(event)
- analytics.onEventAction(Analytics.EVENT_SHARE, event)
+ if (event.location.hotel == null) {
+ map.visibility = View.GONE
+ } else {
+ map.visibility = View.VISIBLE
+ map.setOnClickListener {
+ onMapClick(event)
}
+ }
- star.setOnClickListener {
- onBookmarkClick(event)
- }
+ displayDescription(event)
- displayDescription(event)
+ displayTypes(event)
- displayTypes(event)
+ displayBookmark(event)
- displayBookmark(event)
+ displaySpeakers(event)
- val speakers = displaySpeakers(event)
-// displayRelatedEvents(it, speakers)
+ analytics.onEventAction(Analytics.EVENT_VIEW, event)
+ }
- analytics.onEventAction(Analytics.EVENT_VIEW, event)
- }
+ private fun onMapClick(event: Event) {
+ (context as? MainActivity)?.showMap(event.location)
}
private fun onLinkClick(url: String?) {
@@ -142,6 +165,12 @@ class EventFragment : Fragment() {
event.isBookmarked = !event.isBookmarked
database.updateBookmark(event)
+ if(event.isBookmarked) {
+ reminder.setReminder(event)
+ } else {
+ reminder.cancel(event)
+ }
+
val action = if (event.isBookmarked) Analytics.EVENT_BOOKMARK else Analytics.EVENT_UNBOOKMARK
analytics.onEventAction(action, event)
@@ -149,10 +178,8 @@ class EventFragment : Fragment() {
}
private fun getDetailsDescription(event: Event): String {
-// val context = context ?:
- return ""
-
-// return "Attending ${event.title} at ${getFullTimeStamp(context, event)} in ${event.location.firstOrNull()?.name} #hackertracker"
+ val context = context ?: return ""
+ return "Attending ${event.title} at ${getFullTimeStamp(context, event)} in ${event.location.name} #hackertracker"
}
private fun displayBookmark(event: Event) {
@@ -168,13 +195,6 @@ class EventFragment : Fragment() {
val image = ContextCompat.getDrawable(context, drawable)?.mutate()
- if (isBookmarked && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- event.type.let {
- val color = Color.parseColor(it.color)
- image?.setTint(color)
- }
- }
-
star.setImageDrawable(image)
}
@@ -187,14 +207,14 @@ class EventFragment : Fragment() {
}
- fun getFullTimeStamp(context: Context, event: Event): String {
+ private fun getFullTimeStamp(context: Context, event: Event): String {
val (begin, end) = getTimeStamp(context, event)
- val timestamp = TimeUtil.getRelativeDateStamp(context, event.start)
+ val timestamp = TimeUtil.getDateStamp(event.start)
return String.format(context.getString(R.string.timestamp_full), timestamp, begin, end)
}
- fun getTimeStamp(context: Context, event: Event): Pair {
+ private fun getTimeStamp(context: Context, event: Event): Pair {
val begin = TimeUtil.getTimeStamp(context, event.start)
val end = TimeUtil.getTimeStamp(context, event.end)
return Pair(begin, end)
@@ -206,13 +226,22 @@ class EventFragment : Fragment() {
val type = event.type
val context = context ?: return
- val color = Color.parseColor(type.color)
- app_bar.setBackgroundColor(color)
+ val value = TypedValue()
+ context.theme.resolveAttribute(R.attr.category_tint, value, true)
+ val id = value.resourceId
+
+ val color = if (id > 0) {
+ ContextCompat.getColor(context, id)
+ } else {
+ Color.parseColor(type.color)
+ }
+
+ app_bar.setBackgroundColor(Color.parseColor(type.color))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val drawable = ContextCompat.getDrawable(context, R.drawable.chip_background)?.mutate()
drawable?.setTint(color)
- category_text.background = drawable
+ category_dot.background = drawable
}
category_text.text = type.name
@@ -234,20 +263,4 @@ class EventFragment : Fragment() {
}
}
}
-
-
- private fun displayRelatedEvents(event: Event, speakers: List) {
- val context = context ?: return
-
-// val relatedEvents = database.getRelatedEvents(event.id, event.types, speakers)
-//
-// if (relatedEvents.isNotEmpty()) {
-// related_events_header.visibility = View.VISIBLE
-// relatedEvents.forEach {
-// related_events.addView(EventView(context, it))
-// }
-// } else {
- related_events_header.visibility = View.GONE
-// }
- }
}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/ArticleViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/home/ArticleViewHolder.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/ArticleViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/home/ArticleViewHolder.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HeaderViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/home/HeaderViewHolder.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HeaderViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/home/HeaderViewHolder.kt
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt
new file mode 100644
index 00000000..6068b981
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt
@@ -0,0 +1,112 @@
+package com.shortstack.hackertracker.ui.home
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.shortstack.hackertracker.models.local.Article
+import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.ui.schedule.EventViewHolder
+import com.shortstack.hackertracker.views.EventView
+
+class HomeAdapter : RecyclerView.Adapter() {
+
+ companion object {
+ private const val SKULL = 0
+ private const val HEADER = 1
+ private const val EVENT = 2
+ private const val ARTICLE = 3
+ }
+
+ private val collection = ArrayList()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return when (viewType) {
+ SKULL -> SkullHeaderViewHolder.inflate(parent)
+ HEADER -> HeaderViewHolder.inflate(parent)
+ EVENT -> EventViewHolder.inflate(parent, EventView.DISPLAY_MODE_MIN)
+ ARTICLE -> ArticleViewHolder.inflate(parent)
+ else -> throw IllegalStateException("Unknown viewType $viewType")
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ if (position == 0)
+ return SKULL
+
+
+ return when (collection[position]) {
+ is Article -> ARTICLE
+ is String -> HEADER
+ else -> EVENT
+ }
+ }
+
+ override fun getItemCount() = collection.size
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when (holder) {
+ is EventViewHolder -> holder.render(collection[position] as Event)
+ is ArticleViewHolder -> holder.render(collection[position] as Article)
+ is HeaderViewHolder -> holder.render(collection[position] as String)
+ }
+ }
+
+ fun setElements(list: List) {
+ val list = listOf(SKULL) + list
+
+ val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ if(oldItemPosition == 0 && newItemPosition == 0)
+ return true
+
+ val lhs = collection[oldItemPosition]
+ val rhs = list[newItemPosition]
+
+ if (lhs is Event && rhs is Event) {
+ return lhs.id == rhs.id
+ }
+
+ if (lhs is Article && rhs is Article) {
+ return lhs.id == rhs.id
+ }
+
+ if (lhs is String && rhs is String) {
+ return lhs == rhs
+ }
+
+ return false
+ }
+
+ override fun getOldListSize() = collection.size
+
+ override fun getNewListSize() = list.size
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ if(oldItemPosition == 0 && newItemPosition == 0)
+ return true
+
+ val lhs = collection[oldItemPosition]
+ val rhs = list[newItemPosition]
+
+ if (lhs is Event && rhs is Event) {
+ return lhs.updated == rhs.updated && lhs.isBookmarked == rhs.isBookmarked
+ }
+
+ if (lhs is Article && rhs is Article) {
+ return lhs.name == rhs.name && lhs.text == rhs.text
+ }
+
+ if (lhs is String && rhs is String) {
+ return lhs == rhs
+ }
+
+ return false
+ }
+
+ })
+
+ collection.clear()
+ collection.addAll(list)
+ result.dispatchUpdatesTo(this)
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt
similarity index 58%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt
index 6c55de29..fe1348aa 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/home/HomeFragment.kt
@@ -6,10 +6,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
+import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.shortstack.hackertracker.R
-import kotlinx.android.synthetic.main.fragment_recyclerview.*
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import kotlinx.android.synthetic.main.fragment_home.*
class HomeFragment : Fragment() {
@@ -20,7 +22,7 @@ class HomeFragment : Fragment() {
private val adapter = HomeAdapter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.fragment_recyclerview, container, false)
+ return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -29,9 +31,15 @@ class HomeFragment : Fragment() {
list.adapter = adapter
list.layoutManager = LinearLayoutManager(context)
- val viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
- viewModel.results.observe(this, Observer {
- adapter.setElements(it)
+ toolbar.setNavigationOnClickListener {
+ (context as MainActivity).openNavDrawer()
+ }
+
+
+ val viewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
+ viewModel.home.observe(this, Observer {
+ if (it.data != null)
+ adapter.setElements(it.data)
})
loading_progress.visibility = View.GONE
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/SkullHeaderViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/home/SkullHeaderViewHolder.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/SkullHeaderViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/home/SkullHeaderViewHolder.kt
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt
new file mode 100644
index 00000000..ddd79765
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt
@@ -0,0 +1,115 @@
+package com.shortstack.hackertracker.ui.information
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentStatePagerAdapter
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.tabs.TabLayout
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.Status
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.ui.information.faq.FAQFragment
+import com.shortstack.hackertracker.ui.information.info.InfoFragment
+import com.shortstack.hackertracker.ui.information.speakers.SpeakersFragment
+import com.shortstack.hackertracker.ui.information.vendors.VendorsFragment
+import kotlinx.android.synthetic.main.fragment_information.*
+
+class InformationFragment : Fragment() {
+
+ companion object {
+ private const val INFO = 0
+ private const val FAQ = 1
+ private const val SPEAKERS = 2
+ private const val VENDORS = 3
+
+
+ fun newInstance(): InformationFragment {
+ return InformationFragment()
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_information, container, false)
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+
+ toolbar.setNavigationOnClickListener {
+ (context as MainActivity).openNavDrawer()
+ }
+
+ tabs.apply {
+ tabGravity = TabLayout.GRAVITY_FILL
+ setupWithViewPager(pager)
+ }
+
+ pager.offscreenPageLimit = 4
+
+ tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabReselected(tab: TabLayout.Tab) {}
+
+ override fun onTabUnselected(tab: TabLayout.Tab) {}
+
+ override fun onTabSelected(tab: TabLayout.Tab) {
+ pager.currentItem = tab.position
+ }
+ })
+
+ val viewModel = ViewModelProvider(this)[HackerTrackerViewModel::class.java]
+ viewModel.conference.observe(this, Observer {
+ val fm = activity?.supportFragmentManager ?: return@Observer
+ if (it.status == Status.SUCCESS) {
+ val adapter = PagerAdapter(fm, it.data!!.code)
+ pager.adapter = adapter
+ }
+ })
+ }
+
+ class PagerAdapter(fm: FragmentManager, private val conference: String) : FragmentStatePagerAdapter(fm) {
+ override fun getItem(position: Int): Fragment {
+ val index = if (conference == "DEFCON27") {
+ position
+ } else {
+ position + 1
+ }
+
+ return when (index) {
+ INFO -> InfoFragment.newInstance()
+ SPEAKERS -> SpeakersFragment.newInstance()
+ FAQ -> FAQFragment.newInstance()
+ VENDORS -> VendorsFragment.newInstance()
+ else -> throw IndexOutOfBoundsException("Position out of bounds: $index")
+ }
+ }
+
+ override fun getPageTitle(position: Int): CharSequence? {
+ val index = if (conference == "DEFCON27") {
+ position
+ } else {
+ position + 1
+ }
+
+ return when (index) {
+ INFO -> "Event"
+ SPEAKERS -> "Speakers"
+ FAQ -> "FAQ"
+ VENDORS -> "Vendors"
+ else -> throw IndexOutOfBoundsException("Position out of bounds: $index")
+ }
+ }
+
+ override fun getCount(): Int {
+ if (conference == "DEFCON27")
+ return 4
+ return 3
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQFragment.kt
new file mode 100644
index 00000000..c95b5a77
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQFragment.kt
@@ -0,0 +1,26 @@
+package com.shortstack.hackertracker.ui.information.faq
+
+import android.os.Bundle
+import android.view.View
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.shortstack.hackertracker.models.firebase.FirebaseFAQ
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.ListFragment
+import com.shortstack.hackertracker.ui.activities.MainActivity
+
+class FAQFragment : ListFragment() {
+
+ companion object {
+ fun newInstance() = FAQFragment()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val viewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
+ viewModel.faq.observe(this, Observer {
+ onResource(it)
+ })
+ }
+}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/FAQViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQViewHolder.kt
similarity index 76%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/FAQViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQViewHolder.kt
index b00214d7..27f4b4b2 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/FAQViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/faq/FAQViewHolder.kt
@@ -1,4 +1,4 @@
-package com.shortstack.hackertracker.ui.information
+package com.shortstack.hackertracker.ui.information.faq
import android.view.LayoutInflater
import android.view.View
@@ -8,18 +8,17 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.TransitionManager
-import com.crashlytics.android.answers.CustomEvent
import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.models.local.FAQ
import com.shortstack.hackertracker.utilities.Analytics
-import com.shortstack.hackertracker.models.firebase.FirebaseFAQ
import kotlinx.android.synthetic.main.row_faq.view.*
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
+import org.koin.core.KoinComponent
+import org.koin.core.inject
class FAQViewHolder(val view: View) : RecyclerView.ViewHolder(view), KoinComponent {
companion object {
- fun inflate(parent: ViewGroup): RecyclerView.ViewHolder {
+ fun inflate(parent: ViewGroup): FAQViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_faq, parent, false)
return FAQViewHolder(view)
}
@@ -27,7 +26,7 @@ class FAQViewHolder(val view: View) : RecyclerView.ViewHolder(view), KoinCompone
private val analytics: Analytics by inject()
- fun render(faq: FirebaseFAQ) {
+ fun render(faq: FAQ) {
view.answer.visibility = View.GONE
view.question.text = faq.question
@@ -38,13 +37,15 @@ class FAQViewHolder(val view: View) : RecyclerView.ViewHolder(view), KoinCompone
}
}
- private fun onFAQClick(faq: FirebaseFAQ) {
+ private fun onFAQClick(faq: FAQ) {
val root = view.container
- val isExpanded = view.answer.visibility == View.VISIBLE
+ val isExpanded = faq.isExpanded
+
+ faq.isExpanded = !faq.isExpanded
if (!isExpanded) {
- val event = CustomEvent(Analytics.FAQ_VIEW).also {
+ val event = Analytics.CustomEvent(Analytics.FAQ_VIEW).also {
it.putCustomAttribute("Question", faq.question)
}
analytics.logCustom(event)
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/info/InfoFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/InfoFragment.kt
new file mode 100644
index 00000000..dc867b44
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/InfoFragment.kt
@@ -0,0 +1,36 @@
+package com.shortstack.hackertracker.ui.information.info
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.database.DatabaseManager
+import kotlinx.android.synthetic.main.fragment_info.*
+import org.koin.android.ext.android.inject
+
+class InfoFragment : Fragment() {
+
+ companion object {
+ fun newInstance() = InfoFragment()
+ }
+
+ private val database: DatabaseManager by inject()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_info, container, false)
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+
+ database.conference.observe(this, Observer {
+ conduct.setText(it.conduct)
+ })
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/CodeOfConductView.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/CodeOfConductView.kt
new file mode 100644
index 00000000..f4c52284
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/CodeOfConductView.kt
@@ -0,0 +1,20 @@
+package com.shortstack.hackertracker.ui.information.info.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.shortstack.hackertracker.R
+import kotlinx.android.synthetic.main.view_code_of_conduct.view.*
+
+
+class CodeOfConductView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+ init {
+ inflate(context, R.layout.view_code_of_conduct, this)
+ }
+
+ fun setText(conduct: String?) {
+ content.text = conduct?.replace("\\n", "\n")
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/HelpLineView.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/HelpLineView.kt
new file mode 100644
index 00000000..bcde5ec9
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/info/views/HelpLineView.kt
@@ -0,0 +1,41 @@
+package com.shortstack.hackertracker.ui.information.info.views
+
+import android.app.AlertDialog
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import com.shortstack.hackertracker.R
+import kotlinx.android.synthetic.main.view_help_line.view.*
+
+
+class HelpLineView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+ init {
+ inflate(context, R.layout.view_help_line, this)
+
+ call.setOnClickListener {
+ showCallAlert()
+ }
+ }
+
+ private fun showCallAlert() {
+ AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
+ .setTitle(R.string.help_line_title)
+ .setMessage(R.string.help_line_message)
+ .setNegativeButton(R.string.cancel) { _, _ ->
+ // do nothing
+ }
+ .setPositiveButton(R.string.call) { _, _ ->
+ callHotline()
+ }.show()
+ }
+
+ private fun callHotline() {
+ val intent = Intent(Intent.ACTION_DIAL)
+ intent.data = Uri.parse("tel:+1 (725) 222-0934")
+ context.startActivity(intent)
+ }
+
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerAdapter.kt
similarity index 95%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerAdapter.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerAdapter.kt
index 29cec8b7..b1cef863 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerAdapter.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerAdapter.kt
@@ -1,4 +1,4 @@
-package com.shortstack.hackertracker.ui.speakers
+package com.shortstack.hackertracker.ui.information.speakers
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerFragment.kt
new file mode 100644
index 00000000..55b24efe
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerFragment.kt
@@ -0,0 +1,133 @@
+package com.shortstack.hackertracker.ui.information.speakers
+
+import android.content.Intent
+import android.graphics.Color
+import android.net.Uri
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.utilities.Analytics
+import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.models.local.Speaker
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.views.EventView
+import kotlinx.android.synthetic.main.empty_text.*
+import kotlinx.android.synthetic.main.fragment_speakers.*
+import org.koin.android.ext.android.inject
+
+class SpeakerFragment : Fragment() {
+
+ companion object {
+ private const val EXTRA_SPEAKER = "EXTRA_SPEAKER"
+
+ fun newInstance(speaker: Speaker): SpeakerFragment {
+ val fragment = SpeakerFragment()
+
+ val bundle = Bundle()
+ bundle.putParcelable(EXTRA_SPEAKER, speaker)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+
+ private val database: DatabaseManager by inject()
+ private val analytics: Analytics by inject()
+
+ private val viewModel by lazy { ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java] }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_speakers, container, false)
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+
+ val context = context ?: return
+
+
+ val drawable = ContextCompat.getDrawable(context, R.drawable.ic_arrow_back_white_24dp)
+ toolbar.navigationIcon = drawable
+
+ toolbar.setNavigationOnClickListener {
+ (activity as? MainActivity)?.popBackStack()
+ }
+
+// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+// val height = StatusBarSpacer.getStatusBarHeight(context, app_bar)
+// app_bar.setPadding(0, height, 0, 0)
+// }
+
+
+
+
+ val speaker = arguments?.getParcelable(EXTRA_SPEAKER) as? Speaker
+
+ viewModel.speakers.observe(this, Observer {
+ val target = it.data?.find { it.id == speaker?.id }
+ if(target != null) {
+ showSpeaker(target)
+ }
+ })
+ }
+
+ private fun showSpeaker(speaker: Speaker) {
+ analytics.log("Viewing speaker ${speaker.name}")
+
+ collapsing_toolbar.title = speaker.name
+ collapsing_toolbar.subtitle = if (speaker.title.isEmpty()) {
+ context?.getString(R.string.speaker_default_title)
+ } else {
+ speaker.title
+ }
+
+ val url = speaker.twitter
+ if (url.isEmpty()) {
+ twitter.visibility = View.GONE
+ } else {
+ twitter.visibility = View.VISIBLE
+
+ twitter.setOnClickListener {
+ val url = "https://twitter.com/" + url.replace("@", "")
+
+ val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(url))
+ context?.startActivity(intent)
+
+ analytics.onSpeakerEvent(Analytics.SPEAKER_TWITTER, speaker)
+ }
+ }
+
+
+ val body = speaker.description
+
+ if (body.isNotBlank()) {
+ empty.visibility = View.GONE
+ description.text = body
+ } else {
+ empty.visibility = View.VISIBLE
+ }
+
+ val colours = context!!.resources.getStringArray(R.array.colors)
+ val color = Color.parseColor(colours[speaker.id % colours.size])
+
+ app_bar.setBackgroundColor(color)
+
+ database.getEventsForSpeaker(speaker).observe(this, Observer { list ->
+
+ events_header.visibility = if (list.isEmpty()) View.GONE else View.VISIBLE
+
+ list.forEach {
+ events.addView(EventView(context!!, it, EventView.DISPLAY_MODE_MIN))
+ }
+ })
+
+ analytics.onSpeakerEvent(Analytics.SPEAKER_VIEW, speaker)
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerViewHolder.kt
similarity index 65%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerViewHolder.kt
index bc8ba8a7..d44732d3 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/speakers/SpeakerViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakerViewHolder.kt
@@ -1,9 +1,11 @@
-package com.shortstack.hackertracker.ui.speakers
+package com.shortstack.hackertracker.ui.information.speakers
import android.graphics.Color
+import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.models.local.Speaker
@@ -28,9 +30,18 @@ class SpeakerViewHolder(private val view: View) : RecyclerView.ViewHolder(view)
speaker.title
}
- val colours = context.resources.getStringArray(R.array.colors)
- val color = Color.parseColor(colours[speaker.id % colours.size])
- card.setCardBackgroundColor(color)
+ val value = TypedValue()
+ context.theme.resolveAttribute(R.attr.category_tint, value, true)
+ val id = value.resourceId
+
+ val color = if (id > 0) {
+ ContextCompat.getColor(context, id)
+ } else {
+ val colours = context.resources.getStringArray(R.array.colors)
+ Color.parseColor(colours[speaker.id % colours.size])
+ }
+
+ category.setBackgroundColor(color)
setOnClickListener {
(context as? MainActivity)?.navigate(speaker)
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakersFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakersFragment.kt
new file mode 100644
index 00000000..6a905e76
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/speakers/SpeakersFragment.kt
@@ -0,0 +1,28 @@
+package com.shortstack.hackertracker.ui.information.speakers
+
+import android.os.Bundle
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.shortstack.hackertracker.models.firebase.FirebaseSpeaker
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.ListFragment
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import org.koin.androidx.viewmodel.ext.android.viewModel
+
+class SpeakersFragment : ListFragment() {
+
+ companion object {
+ fun newInstance() = SpeakersFragment()
+ }
+
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+
+ val viewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
+
+ viewModel.speakers.observe(context as MainActivity, Observer {
+ onResource(it)
+ })
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorViewHolder.kt
similarity index 78%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorViewHolder.kt
index 4f7d56ff..864cbd94 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorViewHolder.kt
@@ -1,4 +1,4 @@
-package com.shortstack.hackertracker.ui.vendors
+package com.shortstack.hackertracker.ui.information.vendors
import android.content.Intent
import android.graphics.Color
@@ -8,7 +8,6 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.models.firebase.FirebaseVendor
import com.shortstack.hackertracker.models.local.Vendor
import kotlinx.android.synthetic.main.row_vendor.view.*
@@ -31,10 +30,6 @@ class VendorViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
View.VISIBLE
}
- val colours = view.context.resources.getStringArray(R.array.colors)
- val color = Color.parseColor(colours[vendor.colour % colours.size])
- view.card.setCardBackgroundColor(color)
-
view.link.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(vendor.link))
view.context.startActivity(intent)
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorsFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorsFragment.kt
similarity index 57%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorsFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorsFragment.kt
index cad15b95..b7aa6f31 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/vendors/VendorsFragment.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/information/vendors/VendorsFragment.kt
@@ -1,10 +1,13 @@
-package com.shortstack.hackertracker.ui.vendors
+package com.shortstack.hackertracker.ui.information.vendors
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
import com.shortstack.hackertracker.models.firebase.FirebaseVendor
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
import com.shortstack.hackertracker.ui.ListFragment
+import com.shortstack.hackertracker.ui.activities.MainActivity
class VendorsFragment : ListFragment() {
@@ -16,7 +19,9 @@ class VendorsFragment : ListFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- getViewModel().vendors.observe(this, Observer {
+ val viewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
+
+ viewModel.vendors.observe(this, Observer {
onResource(it)
})
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/maps/MapFragment.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/maps/MapFragment.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt
similarity index 58%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt
index 5a45262d..f1fc4bc2 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/maps/MapsFragment.kt
@@ -8,21 +8,38 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
-import com.crashlytics.android.answers.CustomEvent
+import androidx.lifecycle.ViewModelProvider
import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.models.firebase.FirebaseConferenceMap
+import com.shortstack.hackertracker.models.local.Location
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.utilities.Analytics
import kotlinx.android.synthetic.main.fragment_maps.*
import org.koin.android.ext.android.inject
class MapsFragment : Fragment() {
companion object {
- fun newInstance() = MapsFragment()
+
+ private const val EXTRA_LOCATION = "location"
+
+ fun newInstance(location: Location? = null): MapsFragment {
+ val fragment = MapsFragment()
+
+ if (location != null) {
+ val bundle = Bundle()
+
+ bundle.putParcelable(EXTRA_LOCATION, location)
+ fragment.arguments = bundle
+ }
+
+ return fragment
+ }
}
private val analytics: Analytics by inject()
+ private var isFirstLoad: Boolean = true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_maps, container, false)
@@ -36,10 +53,16 @@ class MapsFragment : Fragment() {
setupWithViewPager(pager)
}
+ toolbar.setNavigationOnClickListener {
+ (context as MainActivity).openNavDrawer()
+ }
- val mapsViewModel = ViewModelProviders.of(this).get(MapsViewModel::class.java)
+
+ val mapsViewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
mapsViewModel.maps.observe(this, Observer {
- when (it.size) {
+ val maps = it.data ?: emptyList()
+
+ when (maps.size) {
0 -> {
tab_layout.visibility = View.GONE
empty_view.visibility = View.VISIBLE
@@ -54,11 +77,27 @@ class MapsFragment : Fragment() {
}
}
- val adapter = PagerAdapter(activity!!.supportFragmentManager, it)
+ val adapter = PagerAdapter(activity!!.supportFragmentManager, maps)
pager.adapter = adapter
+
+ if (isFirstLoad) {
+ isFirstLoad = false
+
+ showSelectedMap(maps)
+ }
+
})
- analytics.logCustom(CustomEvent(Analytics.MAP_VIEW))
+ analytics.logCustom(Analytics.CustomEvent(Analytics.MAP_VIEW))
+ }
+
+ private fun showSelectedMap(it: List) {
+ val location = arguments?.getParcelable(EXTRA_LOCATION)
+ if (location != null) {
+ val position = it.indexOfFirst { it.title == location.hotel }
+ if (position != -1)
+ pager.currentItem = position
+ }
}
class PagerAdapter(fm: FragmentManager, private val maps: List) : FragmentStatePagerAdapter(fm) {
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt
similarity index 64%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt
index 796722d6..910b11f2 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/EventViewHolder.kt
@@ -1,25 +1,25 @@
package com.shortstack.hackertracker.ui.schedule
-import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
-import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.models.local.Event
import com.shortstack.hackertracker.ui.activities.MainActivity
-import kotlinx.android.synthetic.main.row.view.*
+import com.shortstack.hackertracker.views.EventView
class EventViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
companion object {
- fun inflate(parent: ViewGroup): EventViewHolder {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.row, parent, false)
+
+ fun inflate(parent: ViewGroup, mode: Int): EventViewHolder {
+ val view = EventView(parent.context, display = mode)
+ view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
return EventViewHolder(view)
}
}
fun render(event: Event) {
- view.event.setContent(event)
+ (view as EventView).setContent(event)
view.setOnClickListener {
showEventFragment(event)
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt
new file mode 100644
index 00000000..45d175cc
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt
@@ -0,0 +1,236 @@
+package com.shortstack.hackertracker.ui.schedule
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.ViewCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.LinearSmoothScroller
+import androidx.recyclerview.widget.RecyclerView
+import com.advice.timehop.StickyRecyclerHeadersDecoration
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.Status
+import com.shortstack.hackertracker.models.Day
+import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.models.local.Type
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.ui.schedule.list.ScheduleAdapter
+import com.shortstack.hackertracker.views.DaySelectorView
+import kotlinx.android.synthetic.main.fragment_schedule.*
+import kotlinx.android.synthetic.main.fragment_schedule.list
+import kotlinx.android.synthetic.main.view_empty.view.*
+import kotlinx.android.synthetic.main.view_filter.*
+import java.util.*
+
+
+class ScheduleFragment : Fragment() {
+
+ companion object {
+ private const val EXTRA_TYPE = "type"
+
+ fun newInstance(type: Type? = null): ScheduleFragment {
+ val fragment = ScheduleFragment()
+
+ if (type != null) {
+ val bundle = Bundle()
+ bundle.putParcelable(EXTRA_TYPE, type)
+ fragment.arguments = bundle
+ }
+
+ return fragment
+ }
+ }
+
+ private val adapter: ScheduleAdapter = ScheduleAdapter()
+
+ private var shouldScroll = true
+
+ private lateinit var bottomSheet: BottomSheetBehavior
+
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_schedule, container, false) as ViewGroup
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val type = arguments?.getParcelable(EXTRA_TYPE)
+ if (type != null) {
+ toolbar.title = type.name
+ filter.visibility = View.GONE
+ }
+
+ toolbar.inflateMenu(R.menu.schedule)
+ toolbar.setOnMenuItemClickListener {
+ if (it?.itemId == R.id.search) {
+ (context as MainActivity).showSearch()
+ true
+ }
+ false
+ }
+
+ shouldScroll = true
+ list.adapter = adapter
+
+ toolbar.setNavigationOnClickListener {
+ (context as MainActivity).openNavDrawer()
+ }
+
+
+ val decoration = StickyRecyclerHeadersDecoration(adapter)
+ list.addItemDecoration(decoration)
+
+ list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+ val manager = list.layoutManager as? LinearLayoutManager
+ if (manager != null) {
+ val first = manager.findFirstVisibleItemPosition()
+ val last = manager.findLastVisibleItemPosition()
+
+ if (first == -1 || last == -1)
+ return
+
+ day_selector.onScroll(adapter.getDateOfPosition(first), adapter.getDateOfPosition(last))
+ }
+ }
+ })
+
+ day_selector.addOnDaySelectedListener(object : DaySelectorView.OnDaySelectedListener {
+ override fun onDaySelected(day: Date) {
+ scrollToDate(day)
+ }
+ })
+
+
+ val scheduleViewModel = ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java]
+ scheduleViewModel.schedule.observe(this, Observer {
+ hideViews()
+
+ if (it != null) {
+ adapter.state = it.status
+
+ when (it.status) {
+ Status.SUCCESS -> {
+ val list = adapter.setSchedule(it.data)
+ val days = list.filterIsInstance()
+ day_selector.setDays(days)
+
+ if (adapter.isEmpty()) {
+ showEmptyView()
+ }
+
+ scrollToCurrentPosition(list)
+ }
+ Status.ERROR -> {
+ showErrorView(it.message)
+ }
+ Status.LOADING -> {
+ adapter.clearAndNotify()
+ showProgress()
+ }
+ Status.NOT_INITIALIZED -> {
+ showEmptyView()
+ }
+ }
+ }
+ })
+
+ scheduleViewModel.types.observe(this, Observer {
+ filters.setTypes(it.data)
+ })
+
+
+ bottomSheet = BottomSheetBehavior.from(filters)
+ bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
+
+ filter.setOnClickListener { expandFilters() }
+ close.setOnClickListener { hideFilters() }
+
+ ViewCompat.setTranslationZ(filters, 10f)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.search -> (context as MainActivity).showSearch()
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ private fun scrollToCurrentPosition(data: ArrayList) {
+ val manager = list.layoutManager ?: return
+ val first = data.filterIsInstance().firstOrNull { !it.hasStarted } ?: return
+
+ if (shouldScroll) {
+ shouldScroll = false
+ val index = getScrollIndex(data, first)
+ manager.scrollToPosition(index)
+ }
+ }
+
+ private fun getScrollIndex(data: ArrayList, first: Event): Int {
+ val event = data.indexOf(first)
+
+ val element = data.subList(0, event).filterIsInstance().last()
+ val index = data.indexOf(element)
+
+ val x = data.subList(index, event).filterIsInstance().firstOrNull { it.start.time != first.start.time } == null
+ if (!x) {
+ return event
+ }
+
+
+ if (index != -1) {
+ return index
+ }
+ return event
+ }
+
+ private fun scrollToDate(date: Date) {
+ val index = adapter.getDatePosition(date)
+ if (index != -1) {
+ val scroller = object : LinearSmoothScroller(context) {
+
+ override fun getVerticalSnapPreference(): Int {
+ return SNAP_TO_START
+ }
+ }
+ scroller.targetPosition = index
+ list.layoutManager?.startSmoothScroll(scroller)
+ }
+ }
+
+ private fun showProgress() {
+ loading_progress.visibility = View.VISIBLE
+ }
+
+ private fun hideViews() {
+ empty.visibility = View.GONE
+ loading_progress.visibility = View.GONE
+ }
+
+ private fun showEmptyView() {
+ empty.visibility = View.VISIBLE
+ }
+
+ private fun showErrorView(message: String?) {
+ empty.title.text = message
+ empty.visibility = View.VISIBLE
+ }
+
+ private fun expandFilters() {
+ bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED
+ }
+
+ private fun hideFilters() {
+ bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt
similarity index 88%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt
index 8baa3085..c46dbef5 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/TimeViewHolder.kt
@@ -20,8 +20,8 @@ class TimeViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
}
}
- fun render(time: Time) {
- view.time_item.setContent(time)
+ fun render(time: Time?) {
+ view.time_item.render(time)
}
}
@@ -35,6 +35,6 @@ class DayViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
}
fun render(day: Day) {
- (view as TextView).text = TimeUtil.getRelativeDateStamp(view.context, day)
+ (view as TextView).text = TimeUtil.getDateStamp(day)
}
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt
similarity index 67%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt
index 16e7c3fa..ff4d526b 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleAdapter.kt
@@ -3,6 +3,7 @@ package com.shortstack.hackertracker.ui.schedule.list
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
+import com.advice.timehop.StickyRecyclerHeadersAdapter
import com.shortstack.hackertracker.Status
import com.shortstack.hackertracker.models.Day
import com.shortstack.hackertracker.models.Time
@@ -10,24 +11,58 @@ import com.shortstack.hackertracker.models.local.Event
import com.shortstack.hackertracker.ui.schedule.DayViewHolder
import com.shortstack.hackertracker.ui.schedule.EventViewHolder
import com.shortstack.hackertracker.ui.schedule.TimeViewHolder
+import com.shortstack.hackertracker.views.EventView
+import java.util.*
+import kotlin.collections.ArrayList
-class ScheduleAdapter : RecyclerView.Adapter() {
+class ScheduleAdapter : RecyclerView.Adapter(),
+ StickyRecyclerHeadersAdapter {
companion object {
private const val EVENT = 0
private const val DAY = 1
- private const val TIME = 2
}
private val collection = ArrayList()
var state: Status = Status.NOT_INITIALIZED
+ override fun getHeaderId(position: Int): Long {
+ return when (val obj = collection[position]) {
+ // (time - 1) to make it different than the first Event if they both start at 12:00:00
+ is Day -> obj.time - 1
+ is Event -> obj.key
+ else -> throw java.lang.IllegalStateException("Unhandled object type ${obj.javaClass}")
+ }
+ }
+
+ override fun getItemId(position: Int): Long {
+ when (val obj = collection[position]) {
+ is Event -> return obj.key
+ }
+ return super.getItemId(position)
+ }
+
+ override fun onCreateHeaderViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
+ return TimeViewHolder.inflate(parent)
+ }
+
+ override fun onBindHeaderViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
+ val event = (collection[position] as? Event)
+
+ if (viewHolder is TimeViewHolder) {
+ if (event != null) {
+ viewHolder.render(Time(Date(event.key)))
+ } else {
+ viewHolder.render(null)
+ }
+ }
+ }
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
- EVENT -> EventViewHolder.inflate(parent)
+ EVENT -> EventViewHolder.inflate(parent, EventView.DISPLAY_MODE_FULL)
DAY -> DayViewHolder.inflate(parent)
- TIME -> TimeViewHolder.inflate(parent)
else -> throw IllegalStateException("Unknown viewType $viewType.")
}
}
@@ -40,7 +75,6 @@ class ScheduleAdapter : RecyclerView.Adapter() {
when (holder) {
is EventViewHolder -> holder.render(item as Event)
is DayViewHolder -> holder.render(item as Day)
- is TimeViewHolder -> holder.render(item as Time)
}
}
@@ -48,70 +82,28 @@ class ScheduleAdapter : RecyclerView.Adapter() {
return when (collection[position]) {
is Event -> EVENT
is Day -> DAY
- is Time -> TIME
else -> throw IllegalStateException("Unknown viewType ${collection[position].javaClass}")
}
}
private fun getFormattedElements(elements: List): ArrayList {
val result = ArrayList()
-
+
elements.groupBy { it.date }.toSortedMap().forEach {
result.add(Day(it.key))
it.value.groupBy { it.start }.toSortedMap().forEach {
- result.add(Time(it.key))
-
if (it.value.isNotEmpty()) {
val group = it.value.sortedWith(compareBy({ it.type.name }, { it.location.name }))
- result.addAll(group)
- }
- }
- }
-
- return result
- }
-
- fun notifyTimeChanged() {
- if (collection.isEmpty())
- return
- val list = collection.toList()
+ group.forEach { event -> event.key = it.key.time }
- list.forEach {
- if (it is Event && it.hasFinished) {
- removeAndNotify(it)
- }
- }
-
-
- if (list.size != this.collection.size) {
-
- for (i in this.collection.size - 1 downTo 1) {
- val any = this.collection[i]
- val any1 = this.collection[i - 1]
- if ((any is Day && any1 is Day)
- || (any is Time && any1 is Time)
- || (any is Day && any1 is Time)) {
- removeAndNotify(any1)
+ result.addAll(group)
}
}
-
- // If no events and only headers remain.
- if (collection.size == 2) {
- val size = list.size
- collection.clear()
- notifyItemRangeRemoved(0, size)
- }
}
- }
- private fun removeAndNotify(item: Any) {
- val index = collection.indexOf(item)
- if (index != -1) {
- collection.removeAt(index)
- notifyItemRemoved(index)
- }
+ return result
}
fun setSchedule(list: List?): ArrayList {
@@ -133,8 +125,6 @@ class ScheduleAdapter : RecyclerView.Adapter() {
return left.id == right.id
} else if (left is Day && right is Day) {
return left.time == right.time
- } else if (left is Time && right is Time) {
- return left.time == right.time
}
return false
}
@@ -152,8 +142,6 @@ class ScheduleAdapter : RecyclerView.Adapter() {
&& left.title == right.title && left.location.name == right.location.name
} else if (left is Day && right is Day) {
return left.time == right.time
- } else if (left is Time && right is Time) {
- return left.time == right.time
}
return false
}
@@ -169,8 +157,32 @@ class ScheduleAdapter : RecyclerView.Adapter() {
}
fun isEmpty() = state == Status.SUCCESS && collection.isEmpty()
+
fun clearAndNotify() {
collection.clear()
notifyDataSetChanged()
}
+
+ fun getDatePosition(date: Date): Int {
+ val calendar = Calendar.getInstance()
+
+ calendar.time = date
+ calendar.set(Calendar.HOUR_OF_DAY, 0)
+ calendar.set(Calendar.MINUTE, 0)
+ calendar.set(Calendar.SECOND, 0)
+ calendar.set(Calendar.MILLISECOND, 0)
+
+
+ val otherDay = Day(calendar.time)
+
+ return collection.indexOfFirst { it is Day && it.time == otherDay.time }
+ }
+
+ fun getDateOfPosition(index: Int): Date {
+ return when (val obj = collection[index]) {
+ is Event -> obj.start
+ is Day -> Date(obj.time)
+ else -> TODO()
+ }
+ }
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleInfiniteScrollListener.kt b/app/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleInfiniteScrollListener.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleInfiniteScrollListener.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/schedule/list/ScheduleInfiniteScrollListener.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/HeaderViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/search/HeaderViewHolder.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/HeaderViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/search/HeaderViewHolder.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/LocationViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/ui/search/LocationViewHolder.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/LocationViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/search/LocationViewHolder.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt
similarity index 91%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt
index 05aa8e22..0f82af2d 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/search/SearchAdapter.kt
@@ -6,8 +6,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.models.local.Event
import com.shortstack.hackertracker.models.local.Location
import com.shortstack.hackertracker.models.local.Speaker
+import com.shortstack.hackertracker.ui.information.speakers.SpeakerViewHolder
import com.shortstack.hackertracker.ui.schedule.EventViewHolder
-import com.shortstack.hackertracker.ui.speakers.SpeakerViewHolder
+import com.shortstack.hackertracker.views.EventView
class SearchAdapter : RecyclerView.Adapter() {
@@ -23,8 +24,8 @@ class SearchAdapter : RecyclerView.Adapter() {
var query: String? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return when(viewType) {
- EVENT -> EventViewHolder.inflate(parent)
+ return when (viewType) {
+ EVENT -> EventViewHolder.inflate(parent, EventView.DISPLAY_MODE_MIN)
SPEAKER -> SpeakerViewHolder.inflate(parent)
LOCATION -> LocationViewHolder.inflate(parent)
HEADER -> HeaderViewHolder.inflate(parent)
@@ -37,7 +38,7 @@ class SearchAdapter : RecyclerView.Adapter() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = collection[position]
- when(holder) {
+ when (holder) {
is EventViewHolder -> holder.render(item as Event)
is SpeakerViewHolder -> holder.render(item as Speaker)
is LocationViewHolder -> holder.render(item as Location)
@@ -46,7 +47,7 @@ class SearchAdapter : RecyclerView.Adapter() {
}
override fun getItemViewType(position: Int): Int {
- return when(collection[position]) {
+ return when (collection[position]) {
is Speaker -> SPEAKER
is Event -> EVENT
is Location -> LOCATION
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/SearchFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/search/SearchFragment.kt
similarity index 54%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/ui/SearchFragment.kt
rename to app/src/main/java/com/shortstack/hackertracker/ui/search/SearchFragment.kt
index af7f847e..593a83e5 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/SearchFragment.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/search/SearchFragment.kt
@@ -1,27 +1,31 @@
-package com.shortstack.hackertracker.ui
+package com.shortstack.hackertracker.ui.search
+import android.app.Activity
import android.os.Bundle
-import android.view.*
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
+import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.ui.HackerTrackerViewModel
import com.shortstack.hackertracker.ui.activities.MainActivity
-import com.shortstack.hackertracker.ui.search.SearchAdapter
import com.shortstack.hackertracker.ui.search.SearchAdapter.State.*
-import com.shortstack.hackertracker.ui.search.SearchViewModel
-import kotlinx.android.synthetic.main.fragment_recyclerview.*
+import kotlinx.android.synthetic.main.fragment_search.*
-class SearchFragment : Fragment(), SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
+
+class SearchFragment : Fragment(), SearchView.OnQueryTextListener {
companion object {
fun newInstance() = SearchFragment()
}
private val adapter = SearchAdapter()
- private val viewModel by lazy { ViewModelProviders.of(this).get(SearchViewModel::class.java) }
+ private val viewModel by lazy { ViewModelProvider(context as MainActivity)[HackerTrackerViewModel::class.java] }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -29,18 +33,32 @@ class SearchFragment : Fragment(), SearchView.OnQueryTextListener, MenuItem.OnAc
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.fragment_recyclerview, container, false)
+ return inflater.inflate(R.layout.fragment_search, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
-
loading_progress.visibility = View.GONE
empty_view.showDefault()
list.adapter = adapter
- viewModel.results.observe(this, Observer {
+ toolbar.setNavigationOnClickListener {
+ hideKeyboard(context as MainActivity)
+ (context as MainActivity).popBackStack()
+ }
+
+ search.onActionViewExpanded()
+ search.setOnCloseListener {
+ hideKeyboard(context as MainActivity)
+ (context as MainActivity).popBackStack()
+ return@setOnCloseListener true
+ }
+
+ search.setOnQueryTextListener(this)
+
+
+ viewModel.search.observe(this, Observer {
adapter.setList(it)
when (adapter.state) {
@@ -54,33 +72,17 @@ class SearchFragment : Fragment(), SearchView.OnQueryTextListener, MenuItem.OnAc
})
}
- override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
- menu?.findItem(R.id.search)?.apply {
- expandActionView()
- setOnActionExpandListener(this@SearchFragment)
-
- (actionView as SearchView).apply {
- setOnQueryTextListener(this@SearchFragment)
- }
- }
-
- super.onCreateOptionsMenu(menu, inflater)
- }
-
-
override fun onQueryTextSubmit(query: String?) = true
override fun onQueryTextChange(newText: String?): Boolean {
adapter.query = newText
- viewModel.search(newText)
+ viewModel.onQueryTextChange(newText)
return false
}
- override fun onMenuItemActionExpand(item: MenuItem?) = false
-
- override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
- val activity = context as? MainActivity
- activity?.popBackStack()
- return true
+ private fun hideKeyboard(activity: Activity) {
+ val imm = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
+ val view = activity.currentFocus ?: View(activity)
+ imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/settings/SettingsFragment.kt b/app/src/main/java/com/shortstack/hackertracker/ui/settings/SettingsFragment.kt
new file mode 100644
index 00000000..55d8e8c0
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/settings/SettingsFragment.kt
@@ -0,0 +1,96 @@
+package com.shortstack.hackertracker.ui.settings
+
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import com.shortstack.hackertracker.BuildConfig
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.ui.themes.ThemesManager
+import com.shortstack.hackertracker.utilities.Storage
+import kotlinx.android.synthetic.main.fragment_settings.*
+import org.koin.android.ext.android.inject
+
+
+class SettingsFragment : Fragment() {
+
+ companion object {
+ fun newInstance() = SettingsFragment()
+ }
+
+ private val database: DatabaseManager by inject()
+ private val storage: Storage by inject()
+ private val themes: ThemesManager by inject()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_settings, container, false)
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+
+ toolbar.setNavigationOnClickListener {
+ (context as MainActivity).openNavDrawer()
+ }
+
+ // Disabling themes on old devices
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ change_theme.visibility = View.GONE
+ }
+
+ change_theme.setOnClickListener {
+ showChangeThemeDialog()
+ }
+
+ change_conference.setOnClickListener {
+ showChangeConferenceDialog()
+ }
+
+ database.conference.observe(this, Observer {
+ if (it != null) {
+ force_time_zone.text = getString(R.string.setting_time_zone, it.timezone)
+ }
+ })
+
+ version.text = getString(R.string.version, BuildConfig.VERSION_NAME)
+ }
+
+ private fun showChangeConferenceDialog() {
+ val context = context ?: return
+
+ val conferences = database.conferences.value ?: emptyList()
+ val selected = conferences.indexOf(database.conference.value)
+
+ val items = conferences.map { it.name }.toTypedArray()
+
+ AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
+ .setTitle(getString(R.string.choose_conference))
+ .setSingleChoiceItems(items, selected) { dialog, which ->
+ database.changeConference(conferences[which].id)
+ dialog.dismiss()
+ }.show()
+ }
+
+ private fun showChangeThemeDialog() {
+ val context = context ?: return
+
+ val list = themes.getThemes()
+ val selected = list.indexOf(storage.theme)
+
+ val items = list.map { it.label }.toTypedArray()
+
+ AlertDialog.Builder(context, R.style.MyAlertDialogStyle)
+ .setTitle(getString(R.string.choose_theme))
+ .setSingleChoiceItems(items, selected) { dialog, which ->
+ storage.theme = list[which]
+ dialog.dismiss()
+ (context as MainActivity).recreate()
+ }.show()
+ }
+}
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemeContainer.kt b/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemeContainer.kt
new file mode 100644
index 00000000..44f91a11
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemeContainer.kt
@@ -0,0 +1,3 @@
+package com.shortstack.hackertracker.ui.themes
+
+data class ThemeContainer(val label: String, val id: Int)
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemesManager.kt b/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemesManager.kt
new file mode 100644
index 00000000..c0d8d826
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/ui/themes/ThemesManager.kt
@@ -0,0 +1,12 @@
+package com.shortstack.hackertracker.ui.themes
+
+class ThemesManager {
+
+ enum class Theme(val label: String) {
+ Dark("Dark"),
+ Light("Light"),
+ Developer("Advice")
+ }
+
+ fun getThemes() = Theme.values().toList()
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt
similarity index 73%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt
rename to app/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt
index 4e3a9532..936da478 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/Analytics.kt
@@ -1,12 +1,13 @@
package com.shortstack.hackertracker.utilities
-import com.crashlytics.android.Crashlytics
-import com.crashlytics.android.answers.Answers
-import com.crashlytics.android.answers.CustomEvent
+
+import android.content.Context
+import android.os.Bundle
+import com.google.firebase.analytics.FirebaseAnalytics
import com.shortstack.hackertracker.models.local.Event
import com.shortstack.hackertracker.models.local.Speaker
-class Analytics(private val storage: Storage) {
+class Analytics(context: Context, private val storage: Storage) {
companion object {
const val EVENT_VIEW = "Event - View"
@@ -27,6 +28,8 @@ class Analytics(private val storage: Storage) {
const val SETTINGS_EXPIRED_EVENTS = "Settings - Expired Events"
}
+ private val analytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
+
fun onEventAction(action: String, event: Event) {
val event = CustomEvent(action).apply {
putCustomAttribute("Title", event.title)
@@ -57,11 +60,20 @@ class Analytics(private val storage: Storage) {
return
}
- Answers.getInstance().logCustom(event)
+ analytics.logEvent(event.event, event.extras)
}
fun log(message: String) {
- Crashlytics.getInstance().core.log(message)
+ //Crashlytics.getInstance().core.log(message)
+ }
+
+
+ @Deprecated("Replace with Firebase default solution.")
+ class CustomEvent(val event: String, val extras: Bundle = Bundle()) {
+
+ fun putCustomAttribute(key: String, value: String) {
+ extras.putString(key, value)
+ }
}
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/utilities/MyClock.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/MyClock.kt
new file mode 100644
index 00000000..dc27970b
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/MyClock.kt
@@ -0,0 +1,18 @@
+package com.shortstack.hackertracker.utilities
+
+import com.shortstack.hackertracker.BuildConfig
+import java.text.SimpleDateFormat
+import java.util.*
+
+data class MyClock(val value: Int = 0)
+
+fun MyClock.now(): Date {
+ if(BuildConfig.DEBUG) {
+ return parse("2019-06-01T12:00:00.000-0000")
+ }
+ return Date()
+}
+
+private fun parse(date: String): Date {
+ return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").parse(date)
+}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt
similarity index 75%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt
rename to app/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt
index ac9cb7ca..39b5c0c8 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/NotificationHelper.kt
@@ -13,32 +13,30 @@ import android.os.Bundle
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
-import com.firebase.jobdispatcher.FirebaseJobDispatcher
import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.database.DatabaseManager
import com.shortstack.hackertracker.models.local.Event
import com.shortstack.hackertracker.ui.activities.MainActivity
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
+import org.koin.core.KoinComponent
class NotificationHelper(private val context: Context) : KoinComponent {
- private val dispatcher: FirebaseJobDispatcher by inject()
-
- private val database: DatabaseManager by inject()
-
private val manager = NotificationManagerCompat.from(context)
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
-
- val channel = NotificationChannel(CHANNEL_UPDATES, "Schedule Updates", NotificationManager.IMPORTANCE_DEFAULT)
- .apply {
- description = "Notifications about changes within the schedule"
- enableLights(true)
- lightColor = Color.MAGENTA
- }
+ val manager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ val channel = NotificationChannel(
+ CHANNEL_UPDATES,
+ "Schedule Updates",
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ .apply {
+ description = "Notifications about changes within the events"
+ enableLights(true)
+ lightColor = Color.MAGENTA
+ }
manager.createNotificationChannel(channel)
}
@@ -48,7 +46,12 @@ class NotificationHelper(private val context: Context) : KoinComponent {
val builder = notificationBuilder
builder.setContentTitle(item.title)
- builder.setContentText(String.format(context.getString(R.string.notification_text), item.location.name))
+ builder.setContentText(
+ String.format(
+ context.getString(R.string.notification_text),
+ item.location.name
+ )
+ )
setItemPendingIntent(builder, item)
@@ -95,7 +98,8 @@ class NotificationHelper(private val context: Context) : KoinComponent {
intent.putExtras(bundle)
}
- val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(pendingIntent)
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/utilities/StickyHeaderInterface.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/StickyHeaderInterface.kt
new file mode 100644
index 00000000..78330dad
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/StickyHeaderInterface.kt
@@ -0,0 +1,36 @@
+package com.shortstack.hackertracker.utils
+
+import android.view.View
+
+interface StickyHeaderInterface {
+
+ /**
+ * This method gets called by [StickHeaderItemDecoration] to fetch the position of the header item in the adapter
+ * that is used for (represents) item at specified position.
+ * @param itemPosition int. Adapter's position of the item for which to do the onQueryTextChange of the position of the header item.
+ * @return int. Position of the header item in the adapter.
+ */
+ fun getHeaderPositionForItem(itemPosition: Int): Int
+
+ /**
+ * This method gets called by [StickHeaderItemDecoration] to get layout resource id for the header item at specified adapter's position.
+ * @param headerPosition int. Position of the header item in the adapter.
+ * @return int. Layout resource id.
+ */
+ fun getHeaderLayout(headerPosition: Int): Int
+
+ /**
+ * This method gets called by [StickHeaderItemDecoration] to setup the header View.
+ * @param header View. Header to set the data on.
+ * @param headerPosition int. Position of the header item in the adapter.
+ */
+ fun bindHeaderData(header: View, headerPosition: Int)
+
+ /**
+ * This method gets called by [StickHeaderItemDecoration] to verify whether the item represents a header.
+ * @param itemPosition int.
+ * @return true, if item at the specified adapter's position represents a header.
+ */
+ fun isHeader(itemPosition: Int): Boolean
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shortstack/hackertracker/utilities/Storage.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/Storage.kt
new file mode 100644
index 00000000..4c54a75a
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/Storage.kt
@@ -0,0 +1,84 @@
+package com.shortstack.hackertracker.utilities
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.preference.PreferenceManager
+import com.google.gson.Gson
+import com.shortstack.hackertracker.ui.themes.ThemesManager
+
+class Storage(context: Context, private val gson: Gson) {
+
+ companion object {
+ private const val USER_THEME = "user_theme"
+ private const val USER_CONFERENCE = "user_conference"
+
+ private const val EASTER_EGGS = "easter_eggs"
+ private const val NAV_DRAWER_ON_BACK = "nav_drawer_on_back"
+ private const val FORCE_TIME_ZONE = "force_time_zone"
+
+ private const val USER_ALLOW_PUSH = "user_allow_push_notifications"
+ private const val USER_ANALYTICS = "user_analytics"
+ }
+
+ private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+ val allowPushNotification: Boolean
+ get() = preferences.getBoolean(USER_ALLOW_PUSH, true)
+
+ var allowAnalytics: Boolean
+ get() = preferences.getBoolean(USER_ANALYTICS, true)
+ set(value) {
+ preferences.edit().putBoolean(USER_ANALYTICS, value).apply()
+ }
+
+ var navDrawerOnBack: Boolean
+ get() = preferences.getBoolean(NAV_DRAWER_ON_BACK, false)
+ set(value) {
+ preferences.edit().putBoolean(NAV_DRAWER_ON_BACK, value).apply()
+ }
+
+ var forceTimeZone: Boolean
+ get() = preferences.getBoolean(FORCE_TIME_ZONE, true)
+ set(value) {
+ preferences.edit().putBoolean(FORCE_TIME_ZONE, value).apply()
+ }
+
+ var easterEggs: Boolean
+ get() = preferences.getBoolean(EASTER_EGGS, false)
+ set(value) {
+ preferences.edit().putBoolean(EASTER_EGGS, value).apply()
+ }
+
+
+ var preferredConference: Int
+ get() = preferences.getInt(USER_CONFERENCE, -1)
+ set(value) {
+ preferences.edit().putInt(USER_CONFERENCE, value).apply()
+ }
+
+ var theme: ThemesManager.Theme?
+ get() = gson.fromJson(preferences.getString(USER_THEME, ""), ThemesManager.Theme::class.java)
+ set(value) {
+ preferences.edit().putString(USER_THEME, gson.toJson(value)).apply()
+ }
+
+ fun setPreference(key: String, isChecked: Boolean) {
+ when (key) {
+ USER_ANALYTICS -> allowAnalytics = isChecked
+ NAV_DRAWER_ON_BACK -> navDrawerOnBack = isChecked
+ FORCE_TIME_ZONE -> forceTimeZone = isChecked
+ EASTER_EGGS -> easterEggs = isChecked
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+
+ fun getPreference(key: String, defaultValue: Boolean): Boolean {
+ return when (key) {
+ USER_ANALYTICS -> allowAnalytics
+ NAV_DRAWER_ON_BACK -> navDrawerOnBack
+ FORCE_TIME_ZONE -> forceTimeZone
+ EASTER_EGGS -> easterEggs
+ else -> throw IllegalArgumentException("Unknown key: $key")
+ }
+ }
+}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt b/app/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt
similarity index 51%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt
rename to app/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt
index 745f35c5..15bb43ee 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/utilities/TimeUtil.kt
@@ -2,22 +2,15 @@ package com.shortstack.hackertracker.utilities
import android.annotation.SuppressLint
import android.content.Context
-import com.shortstack.hackertracker.*
+import com.shortstack.hackertracker.App
+import com.shortstack.hackertracker.R
import java.text.SimpleDateFormat
import java.util.*
object TimeUtil {
- private const val SOON_DAYS_AMOUNT = 5
-
@SuppressLint("SimpleDateFormat")
- fun getRelativeDateStamp(context: Context, date: Date): String {
- if (date.isToday())
- return context.getString(R.string.today)
-
- if (date.isTomorrow())
- return context.getString(R.string.tomorrow)
-
+ fun getDateStamp(date: Date): String {
val format = SimpleDateFormat("MMMM d")
return format.format(date)
@@ -30,10 +23,20 @@ object TimeUtil {
if (date == null)
return context.getString(R.string.tba)
- return if (android.text.format.DateFormat.is24HourFormat(context)) {
- SimpleDateFormat("HH:mm").format(date)
+
+ val s = if (android.text.format.DateFormat.is24HourFormat(context)) {
+ "HH:mm"
} else {
- SimpleDateFormat("h:mm aa").format(date)
+ "h:mm\naa"
}
+
+ val formatter = SimpleDateFormat(s)
+
+ if (App.instance.storage.forceTimeZone) {
+ val timezone = App.instance.database.conference.value?.timezone ?: "America/Los_Angeles"
+ formatter.timeZone = TimeZone.getTimeZone(timezone)
+ }
+
+ return formatter.format(date)
}
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/views/DaySelectorView.kt b/app/src/main/java/com/shortstack/hackertracker/views/DaySelectorView.kt
new file mode 100644
index 00000000..e14188a5
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/views/DaySelectorView.kt
@@ -0,0 +1,200 @@
+package com.shortstack.hackertracker.views
+
+import android.animation.ObjectAnimator
+import android.app.Activity
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.view.animation.AnticipateOvershootInterpolator
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.transition.ChangeBounds
+import androidx.transition.TransitionManager
+import com.shortstack.hackertracker.R
+import kotlinx.android.synthetic.main.view_day_selector.view.*
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.collections.ArrayList
+
+
+class DaySelectorView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+ private val children = ArrayList()
+ private var listener: OnDaySelectedListener? = null
+
+ private var begin: Int = -1
+ private var end: Int = -1
+
+ init {
+ View.inflate(context, R.layout.view_day_selector, this)
+
+ for (i in 0..frame.childCount) {
+ val view = frame.getChildAt(i)
+ if (view is TextView) {
+ children.add(view)
+ view.setOnClickListener {
+ val tag = view.tag as? Long
+ if( tag != null) {
+ listener?.onDaySelected(Date(tag))
+ }
+ }
+ }
+ }
+ }
+
+ private fun onDaySelected(view: View, position: Int) {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(frame)
+
+ constraintSet.apply {
+ connect(bubble.id, position, view.id, position)
+ }
+
+
+ val transition = ChangeBounds().apply {
+ interpolator = AnticipateOvershootInterpolator(1.0f)
+ duration = 500
+ }
+
+ TransitionManager.beginDelayedTransition(frame, transition)
+ constraintSet.applyTo(frame)
+ }
+
+ fun onScroll(begin: Date, end: Date) {
+ val dates = children.map { Date(it.tag as Long) }
+
+ val instance = Calendar.getInstance()
+
+ instance.time = begin
+ instance.set(Calendar.HOUR_OF_DAY, 0)
+ instance.set(Calendar.MINUTE, 0)
+ instance.set(Calendar.SECOND, 0)
+ instance.set(Calendar.MILLISECOND, 0)
+
+ val beginDay = instance.time
+
+ instance.time = end
+ instance.set(Calendar.HOUR_OF_DAY, 0)
+ instance.set(Calendar.MINUTE, 0)
+ instance.set(Calendar.SECOND, 0)
+ instance.set(Calendar.MILLISECOND, 0)
+
+ val endDay = instance.time
+
+
+ val beginIndex = getDayIndex(dates, beginDay)
+ if(beginIndex != -1) {
+ val view = getViewByIndex(beginIndex)
+ onBeginDaySelected(view)
+ setCenter(view)
+ }
+
+ val endIndex = getDayIndex(dates, endDay)
+ if (endIndex != -1) {
+ onEndDaySelected(getViewByIndex(endIndex))
+ }
+
+
+ }
+
+ private fun setCenter(view: View) {
+ val screenWidth = (context as Activity).windowManager
+ .defaultDisplay.width
+
+ val scrollX = view.left - screenWidth / 2 + view.width / 2
+
+ val animator = ObjectAnimator.ofInt(root, "scrollX", scrollX)
+ animator.duration = 300
+ animator.start()
+ }
+
+ private fun getViewByIndex(index: Int): View {
+ return when (index) {
+ 0 -> day_1
+ 1 -> day_2
+ 2 -> day_3
+ 3 -> day_4
+ 4 -> day_5
+ 5 -> day_6
+ 6 -> day_7
+ 7 -> day_8
+ 8 -> day_9
+ 9 -> day_10
+ 10 -> day_11
+ 11 -> day_12
+ else -> throw ArrayIndexOutOfBoundsException("Index out of bounds: $index.")
+ }
+ }
+
+ private fun getDayIndex(dates: List, endDay: Date): Int {
+ return dates.indexOfFirst {
+ it.time == endDay.time
+ }
+ }
+
+ private fun onBeginDaySelected(view: View) {
+ if (begin == view.id)
+ return
+
+ begin = view.id
+
+ onDaySelected(view, START)
+ }
+
+ private fun onEndDaySelected(view: View) {
+ if (end == view.id)
+ return
+
+ end = view.id
+
+ onDaySelected(view, END)
+ }
+
+ fun addOnDaySelectedListener(listener: OnDaySelectedListener) {
+ this.listener = listener
+ }
+
+ fun setDays(days: List) {
+ val calendar = Calendar.getInstance()
+
+ days.forEach {
+ calendar.time = it
+ calendar.set(Calendar.HOUR_OF_DAY, 0)
+ calendar.set(Calendar.MINUTE, 0)
+ calendar.set(Calendar.SECOND, 0)
+ calendar.set(Calendar.MILLISECOND, 0)
+
+ it.time = calendar.time.time
+ }
+
+
+ children.clear()
+
+ val format = SimpleDateFormat("MMM d")
+
+
+ for (index in 0..11) {
+ val view = getViewByIndex(index) as TextView
+
+ if (index < days.size) {
+ val date = days[index]
+
+ view.visibility = View.VISIBLE
+ view.text = format.format(date)
+ view.tag = date.time
+
+ children.add(view)
+ } else {
+ view.visibility = View.GONE
+ }
+ }
+ }
+
+ interface OnDaySelectedListener {
+ fun onDaySelected(day: Date)
+ }
+
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt b/app/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt
similarity index 96%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt
index 2d6a0135..a0f60718 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/EmptyView.kt
@@ -8,7 +8,7 @@ import android.widget.TextView
import com.shortstack.hackertracker.R
import kotlinx.android.synthetic.main.view_empty.view.*
-class EmptyView(context: Context?, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+class EmptyView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
init {
View.inflate(context, R.layout.view_empty, this)
diff --git a/app/src/main/java/com/shortstack/hackertracker/views/EventView.kt b/app/src/main/java/com/shortstack/hackertracker/views/EventView.kt
new file mode 100644
index 00000000..792fc404
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/views/EventView.kt
@@ -0,0 +1,152 @@
+package com.shortstack.hackertracker.views
+
+import android.content.Context
+import android.graphics.Color
+import android.os.Build
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.content.ContextCompat
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.database.DatabaseManager
+import com.shortstack.hackertracker.database.ReminderManager
+import com.shortstack.hackertracker.models.local.Event
+import com.shortstack.hackertracker.ui.activities.MainActivity
+import com.shortstack.hackertracker.utilities.Analytics
+import kotlinx.android.synthetic.main.row_event.view.*
+import org.koin.core.KoinComponent
+import org.koin.core.inject
+
+class EventView : FrameLayout, KoinComponent {
+
+ companion object {
+ const val DISPLAY_MODE_MIN = 0
+ const val DISPLAY_MODE_FULL = 1
+ }
+
+ private val analytics: Analytics by inject()
+ private val database: DatabaseManager by inject()
+ private val reminder: ReminderManager by inject()
+
+ var displayMode: Int = DISPLAY_MODE_MIN
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ display: Int = DISPLAY_MODE_MIN
+ ) : super(context, attrs) {
+ displayMode = display
+ init()
+ }
+
+ constructor(context: Context, event: Event, display: Int = DISPLAY_MODE_FULL) : super(context) {
+ displayMode = display
+ init()
+ setContent(event)
+ }
+
+ private fun init() {
+ inflate(context, R.layout.row_event, this)
+ setDisplayMode()
+ }
+
+ fun setContent(event: Event) {
+ render(event)
+ }
+
+ private fun setDisplayMode() {
+ when (displayMode) {
+ DISPLAY_MODE_MIN -> {
+ val width = context.resources.getDimension(R.dimen.event_view_min_guideline).toInt()
+ guideline.setGuidelineBegin(width)
+ category_dot.visibility = View.GONE
+ category_text.visibility = View.GONE
+ }
+ DISPLAY_MODE_FULL -> {
+ val width = context.resources.getDimension(R.dimen.time_width).toInt()
+ guideline.setGuidelineBegin(width)
+ category_dot.visibility = View.VISIBLE
+ category_text.visibility = View.VISIBLE
+ }
+ }
+ }
+
+ private fun render(event: Event) {
+ title.text = event.title
+
+ // Stage 2
+ if (displayMode == DISPLAY_MODE_FULL) {
+ location.text = event.location.name
+ } else {
+ location.text = event.getFullTimeStamp(context) + " / " + event.location.name
+ }
+
+
+ renderCategoryColour(event)
+ updateBookmark(event)
+
+ setOnClickListener {
+ (context as? MainActivity)?.navigate(event)
+ }
+
+ star_bar.setOnClickListener {
+ onBookmarkClick(event)
+ }
+ }
+
+ private fun renderCategoryColour(event: Event) {
+ val type = event.type
+
+ category_text.text = type.name
+
+ val value = TypedValue()
+ context.theme.resolveAttribute(R.attr.category_tint, value, true)
+ val id = value.resourceId
+
+ val color = if (id > 0) {
+ ContextCompat.getColor(context, id)
+ } else {
+ Color.parseColor(type.color)
+ }
+ category.setBackgroundColor(color)
+
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val drawable = ContextCompat.getDrawable(context, R.drawable.chip_background)?.mutate()
+
+ drawable?.setTint(color)
+ category_dot.background = drawable
+ }
+ }
+
+
+ private fun updateBookmark(event: Event) {
+ val isBookmarked = event.isBookmarked
+
+ val drawable = if (isBookmarked) {
+ R.drawable.ic_star_accent_24dp
+ } else {
+ R.drawable.ic_star_border_white_24dp
+ }
+
+ star_bar.setImageResource(drawable)
+ }
+
+ private fun onBookmarkClick(event: Event) {
+ event.isBookmarked = !event.isBookmarked
+ database.updateBookmark(event)
+
+ if (event.isBookmarked) {
+ reminder.setReminder(event)
+ } else {
+ reminder.cancel(event)
+ }
+
+ val action =
+ if (event.isBookmarked) Analytics.EVENT_BOOKMARK else Analytics.EVENT_UNBOOKMARK
+ analytics.onEventAction(action, event)
+
+ updateBookmark(event)
+ }
+}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/FilterAdapter.kt b/app/src/main/java/com/shortstack/hackertracker/views/FilterAdapter.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/FilterAdapter.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/FilterAdapter.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/FilterView.kt b/app/src/main/java/com/shortstack/hackertracker/views/FilterView.kt
similarity index 64%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/FilterView.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/FilterView.kt
index 37981a04..3ef0c513 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/FilterView.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/FilterView.kt
@@ -9,9 +9,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.models.local.Type
import kotlinx.android.synthetic.main.view_filter.view.*
-import org.koin.standalone.KoinComponent
-class FilterView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs), KoinComponent {
+class FilterView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
companion object {
private const val SPAN_COUNT = 2
@@ -32,21 +31,22 @@ class FilterView(context: Context, attrs: AttributeSet) : LinearLayout(context,
collection.add(context.getString(R.string.types))
- val elements = types.filter { !it.isBookmark && !it.filtered }
+ val elements = types.filter { !it.isBookmark }
collection.addAll(elements)
val adapter = FilterAdapter(collection)
- list.layoutManager = GridLayoutManager(context, SPAN_COUNT, RecyclerView.VERTICAL, false).apply {
- spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
- override fun getSpanSize(position: Int): Int {
- return when (adapter.getItemViewType(position)) {
- FilterAdapter.TYPE_HEADER -> SPAN_COUNT
- else -> 1
+ list.layoutManager =
+ GridLayoutManager(context, SPAN_COUNT, RecyclerView.VERTICAL, false).apply {
+ spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int): Int {
+ return when (adapter.getItemViewType(position)) {
+ FilterAdapter.TYPE_HEADER -> SPAN_COUNT
+ else -> 1
+ }
}
}
}
- }
list.adapter = adapter
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/NonSwipeableViewPager.kt b/app/src/main/java/com/shortstack/hackertracker/views/NonSwipeableViewPager.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/NonSwipeableViewPager.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/NonSwipeableViewPager.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/ScrollingFABBehavior.kt b/app/src/main/java/com/shortstack/hackertracker/views/ScrollingFABBehavior.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/ScrollingFABBehavior.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/ScrollingFABBehavior.kt
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt b/app/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt
similarity index 89%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt
index 0f6fd408..230eb84c 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/SettingsActionView.kt
@@ -23,10 +23,9 @@ class SettingsActionView(context: Context?, attrs: AttributeSet?) : LinearLayout
}
}
-
- override fun setOnClickListener(listener: OnClickListener) {
+ override fun setOnClickListener(listener: OnClickListener?) {
control_overlay.setOnClickListener {
- listener.onClick(it)
+ listener?.onClick(it)
}
}
}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt b/app/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt
similarity index 57%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt
index fb03e48b..ce044354 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/SettingsSwitchView.kt
@@ -7,25 +7,46 @@ import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.utilities.Analytics
import com.shortstack.hackertracker.utilities.Storage
import kotlinx.android.synthetic.main.view_settings_switch.view.*
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
+import org.koin.core.KoinComponent
+import org.koin.core.inject
-class SettingsSwitchView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs), KoinComponent {
+class SettingsSwitchView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs),
+ KoinComponent {
private val storage: Storage by inject()
private val analytics: Analytics by inject()
+ var text: String
+ get() = label.text.toString()
+ set(value) {
+ label.text = value
+ }
+
init {
inflate(context, R.layout.view_settings_switch, this)
context?.theme?.obtainStyledAttributes(
- attrs,
- R.styleable.SettingsSwitchView,
- 0, 0)?.apply {
+ attrs,
+ R.styleable.SettingsSwitchView,
+ 0, 0
+ )?.apply {
try {
- label.text = getString(R.styleable.SettingsSwitchView_switchText)
- control.tag = getString(R.styleable.SettingsSwitchView_switchKey)
- control.isChecked = getBoolean(R.styleable.SettingsSwitchView_switchDefaultValue, true)
+
+ val text = getString(R.styleable.SettingsSwitchView_switchText)
+ val defaultValue =
+ getBoolean(R.styleable.SettingsSwitchView_switchDefaultValue, true)
+ val key = getString(R.styleable.SettingsSwitchView_switchKey) ?: ""
+
+ label.text = text
+ control.tag = key
+ control.id = key.hashCode()
+
+ if (!isInEditMode) {
+ val isChecked = storage.getPreference(key, defaultValue)
+ control.isChecked = isChecked
+ } else {
+ control.isChecked = defaultValue
+ }
} finally {
recycle()
}
@@ -37,9 +58,9 @@ class SettingsSwitchView(context: Context?, attrs: AttributeSet?) : LinearLayout
}
- override fun setOnClickListener(listener: OnClickListener) {
+ override fun setOnClickListener(listener: OnClickListener?) {
control_overlay.setOnClickListener {
- listener.onClick(it)
+ listener?.onClick(it)
onClick()
}
}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt b/app/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt
similarity index 68%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt
index 632b263d..3c884e1b 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/SpeakerView.kt
@@ -3,8 +3,10 @@ package com.shortstack.hackertracker.views
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
+import android.util.TypedValue
import android.view.LayoutInflater
import android.widget.LinearLayout
+import androidx.core.content.ContextCompat
import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.models.local.Speaker
import com.shortstack.hackertracker.ui.activities.MainActivity
@@ -22,9 +24,18 @@ class SpeakerView : LinearLayout {
speaker.title
}
- val colours = context.resources.getStringArray(R.array.colors)
- val color = Color.parseColor(colours[speaker.id % colours.size])
- card.setCardBackgroundColor(color)
+ val value = TypedValue()
+ context.theme.resolveAttribute(R.attr.category_tint, value, true)
+ val id = value.resourceId
+
+ val color = if (id > 0) {
+ ContextCompat.getColor(context, id)
+ } else {
+ val colours = context.resources.getStringArray(R.array.colors)
+ Color.parseColor(colours[speaker.id % colours.size])
+ }
+
+ category.setBackgroundColor(color)
setOnClickListener {
(context as? MainActivity)?.navigate(speaker)
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/StatusBarSpacer.kt b/app/src/main/java/com/shortstack/hackertracker/views/StatusBarSpacer.kt
similarity index 100%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/StatusBarSpacer.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/StatusBarSpacer.kt
diff --git a/app/src/main/java/com/shortstack/hackertracker/views/TimeView.kt b/app/src/main/java/com/shortstack/hackertracker/views/TimeView.kt
new file mode 100644
index 00000000..a93c84c1
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/views/TimeView.kt
@@ -0,0 +1,26 @@
+package com.shortstack.hackertracker.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.shortstack.hackertracker.R
+import com.shortstack.hackertracker.utilities.TimeUtil
+import kotlinx.android.synthetic.main.row_header_time.view.*
+import java.util.*
+
+class TimeView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
+
+ init {
+ inflate(context, R.layout.row_header_time, this)
+ orientation = VERTICAL
+ }
+
+ fun render(date: Date?) {
+ if(date == null) {
+ header.text = null
+ } else {
+ header.text = TimeUtil.getTimeStamp(context, date)
+ }
+ }
+
+}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt b/app/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt
similarity index 73%
rename from hackertracker/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt
rename to app/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt
index 82890864..ca264771 100644
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt
+++ b/app/src/main/java/com/shortstack/hackertracker/views/TypeViewHolder.kt
@@ -9,18 +9,18 @@ import androidx.recyclerview.widget.RecyclerView
import com.shortstack.hackertracker.R
import com.shortstack.hackertracker.database.DatabaseManager
import com.shortstack.hackertracker.models.local.Type
-import io.reactivex.Single
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.item_type.view.*
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.koin.core.KoinComponent
+import org.koin.core.inject
class TypeViewHolder(val view: View) : RecyclerView.ViewHolder(view), KoinComponent {
companion object {
fun inflate(parent: ViewGroup): TypeViewHolder {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.item_type, parent, false)
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.item_type, parent, false)
return TypeViewHolder(view)
}
}
@@ -44,12 +44,11 @@ class TypeViewHolder(val view: View) : RecyclerView.ViewHolder(view), KoinCompon
chip.isCloseIconEnabled = isChecked
- Single.fromCallable {
+ // todo: put this on the right scope
+ GlobalScope.launch {
type.isSelected = isChecked
database.updateTypeIsSelected(type)
- }.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe({}, {})
+ }
}
}
}
diff --git a/app/src/main/java/com/shortstack/hackertracker/views/WiFiHelperView.kt b/app/src/main/java/com/shortstack/hackertracker/views/WiFiHelperView.kt
new file mode 100644
index 00000000..7f70ed86
--- /dev/null
+++ b/app/src/main/java/com/shortstack/hackertracker/views/WiFiHelperView.kt
@@ -0,0 +1,93 @@
+package com.shortstack.hackertracker.views
+
+import android.annotation.TargetApi
+import android.content.Context
+import android.net.wifi.WifiConfiguration
+import android.net.wifi.WifiConfiguration.*
+import android.net.wifi.WifiEnterpriseConfig
+import android.net.wifi.WifiManager
+import android.os.Build
+import android.util.AttributeSet
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.Toast
+import com.shortstack.hackertracker.R
+import kotlinx.android.synthetic.main.view_wifi_helper.view.*
+import java.security.cert.CertificateFactory
+import java.security.cert.X509Certificate
+
+
+class WiFiHelperView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+ init {
+ inflate(context, R.layout.view_wifi_helper, this)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ save.visibility = View.VISIBLE
+ save.setOnClickListener { connectWifi() }
+ } else {
+ content.text = context.getString(R.string.wifi_content_legacy)
+ save.visibility = View.GONE
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+ private fun connectWifi() {
+ val wifi = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
+
+ wifi.isWifiEnabled = true
+
+
+ val exists = wifi.configuredNetworks.find { it.SSID == "\"DefCon\"" } != null
+
+ val config = WifiConfiguration().apply {
+ SSID = "\"DefCon\""
+ hiddenSSID = false
+ priority = 40
+ status = Status.ENABLED
+
+ allowedKeyManagement.clear()
+ allowedKeyManagement.set(KeyMgmt.WPA_EAP)
+
+ allowedGroupCiphers.clear()
+ allowedGroupCiphers.set(GroupCipher.CCMP)
+ allowedGroupCiphers.set(GroupCipher.TKIP)
+ allowedGroupCiphers.set(GroupCipher.WEP104)
+
+ allowedPairwiseCiphers.clear()
+ allowedPairwiseCiphers.set(PairwiseCipher.CCMP)
+
+ allowedAuthAlgorithms.clear()
+ allowedAuthAlgorithms.set(AuthAlgorithm.OPEN)
+
+ allowedProtocols.clear()
+ allowedProtocols.set(Protocol.RSN)
+
+
+ val x509Certificate = CertificateFactory.getInstance("X.509").generateCertificate(resources.openRawResource(R.raw.digirootonly)) as X509Certificate
+
+ enterpriseConfig = WifiEnterpriseConfig().apply {
+ identity = "defcon"
+ password = "defcon"
+ anonymousIdentity = "anonymous"
+ eapMethod = WifiEnterpriseConfig.Eap.PEAP
+ phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2
+ caCertificate = x509Certificate
+ subjectMatch = "/CN=wifireg.defcon.org"
+ }
+ }
+
+ val network = if (exists) {
+ wifi.updateNetwork(config)
+ } else {
+ wifi.addNetwork(config)
+ }
+
+ val result = wifi.enableNetwork(network, true)
+ if (result) {
+ Toast.makeText(context, "Wifi network saved", Toast.LENGTH_SHORT).show()
+ } else {
+ Toast.makeText(context, "Could not save network", Toast.LENGTH_SHORT).show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/main/res/drawable-hdpi/skull.png b/app/src/main/res/drawable-hdpi/skull.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-hdpi/skull.png
rename to app/src/main/res/drawable-hdpi/skull.png
diff --git a/hackertracker/src/main/res/drawable-hdpi/skull_lg.png b/app/src/main/res/drawable-hdpi/skull_lg.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-hdpi/skull_lg.png
rename to app/src/main/res/drawable-hdpi/skull_lg.png
diff --git a/hackertracker/src/main/res/drawable-ldpi/skull.png b/app/src/main/res/drawable-ldpi/skull.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-ldpi/skull.png
rename to app/src/main/res/drawable-ldpi/skull.png
diff --git a/hackertracker/src/main/res/drawable-ldpi/skull_lg.png b/app/src/main/res/drawable-ldpi/skull_lg.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-ldpi/skull_lg.png
rename to app/src/main/res/drawable-ldpi/skull_lg.png
diff --git a/hackertracker/src/main/res/drawable-mdpi/skull.png b/app/src/main/res/drawable-mdpi/skull.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-mdpi/skull.png
rename to app/src/main/res/drawable-mdpi/skull.png
diff --git a/hackertracker/src/main/res/drawable-mdpi/skull_lg.png b/app/src/main/res/drawable-mdpi/skull_lg.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-mdpi/skull_lg.png
rename to app/src/main/res/drawable-mdpi/skull_lg.png
diff --git a/hackertracker/src/main/res/drawable/article_background.xml b/app/src/main/res/drawable-v21/article_background.xml
similarity index 80%
rename from hackertracker/src/main/res/drawable/article_background.xml
rename to app/src/main/res/drawable-v21/article_background.xml
index e491eb7c..6edbf68c 100644
--- a/hackertracker/src/main/res/drawable/article_background.xml
+++ b/app/src/main/res/drawable-v21/article_background.xml
@@ -3,5 +3,5 @@
+ android:color="?border_tint" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/bottom_sheet_background.xml b/app/src/main/res/drawable-v21/bottom_sheet_background.xml
new file mode 100644
index 00000000..4ec85ae0
--- /dev/null
+++ b/app/src/main/res/drawable-v21/bottom_sheet_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/day_selector_bubble.xml b/app/src/main/res/drawable-v21/day_selector_bubble.xml
new file mode 100644
index 00000000..c4e3d17d
--- /dev/null
+++ b/app/src/main/res/drawable-v21/day_selector_bubble.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/drawable-xhdpi/skull.png b/app/src/main/res/drawable-xhdpi/skull.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-xhdpi/skull.png
rename to app/src/main/res/drawable-xhdpi/skull.png
diff --git a/hackertracker/src/main/res/drawable-xhdpi/skull_lg.png b/app/src/main/res/drawable-xhdpi/skull_lg.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-xhdpi/skull_lg.png
rename to app/src/main/res/drawable-xhdpi/skull_lg.png
diff --git a/app/src/main/res/drawable-xxhdpi/doggo.png b/app/src/main/res/drawable-xxhdpi/doggo.png
new file mode 100644
index 00000000..2d51e13b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/doggo.png differ
diff --git a/hackertracker/src/main/res/drawable-xxhdpi/icon_shadow.png b/app/src/main/res/drawable-xxhdpi/icon_shadow.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-xxhdpi/icon_shadow.png
rename to app/src/main/res/drawable-xxhdpi/icon_shadow.png
diff --git a/hackertracker/src/main/res/drawable-xxhdpi/skull.png b/app/src/main/res/drawable-xxhdpi/skull.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-xxhdpi/skull.png
rename to app/src/main/res/drawable-xxhdpi/skull.png
diff --git a/hackertracker/src/main/res/drawable-xxhdpi/skull_lg.png b/app/src/main/res/drawable-xxhdpi/skull_lg.png
similarity index 100%
rename from hackertracker/src/main/res/drawable-xxhdpi/skull_lg.png
rename to app/src/main/res/drawable-xxhdpi/skull_lg.png
diff --git a/app/src/main/res/drawable/article_background.xml b/app/src/main/res/drawable/article_background.xml
new file mode 100644
index 00000000..220c4988
--- /dev/null
+++ b/app/src/main/res/drawable/article_background.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/drawable/bottom_sheet_background.xml b/app/src/main/res/drawable/bottom_sheet_background.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/bottom_sheet_background.xml
rename to app/src/main/res/drawable/bottom_sheet_background.xml
diff --git a/app/src/main/res/drawable/chip_background.xml b/app/src/main/res/drawable/chip_background.xml
new file mode 100644
index 00000000..5619167b
--- /dev/null
+++ b/app/src/main/res/drawable/chip_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/day_selector_bubble.xml b/app/src/main/res/drawable/day_selector_bubble.xml
new file mode 100644
index 00000000..e9de015c
--- /dev/null
+++ b/app/src/main/res/drawable/day_selector_bubble.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/drawable/ic_arrow_back_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml
similarity index 83%
rename from hackertracker/src/main/res/drawable/ic_arrow_back_white_24dp.xml
rename to app/src/main/res/drawable/ic_arrow_back_white_24dp.xml
index 71d5bbd2..e1d335a4 100644
--- a/hackertracker/src/main/res/drawable/ic_arrow_back_white_24dp.xml
+++ b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml
@@ -1,4 +1,4 @@
-
diff --git a/hackertracker/src/main/res/drawable/ic_cake_white_24dp.xml b/app/src/main/res/drawable/ic_cake_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_cake_white_24dp.xml
rename to app/src/main/res/drawable/ic_cake_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_chevron_right_white_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_chevron_right_white_24dp.xml
rename to app/src/main/res/drawable/ic_chevron_right_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_close_white_24dp.xml b/app/src/main/res/drawable/ic_close_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_close_white_24dp.xml
rename to app/src/main/res/drawable/ic_close_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_computer_white_24dp.xml b/app/src/main/res/drawable/ic_computer_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_computer_white_24dp.xml
rename to app/src/main/res/drawable/ic_computer_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_expand_more_white_24dp.xml b/app/src/main/res/drawable/ic_expand_more_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_expand_more_white_24dp.xml
rename to app/src/main/res/drawable/ic_expand_more_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_filter_list_white_24dp.xml b/app/src/main/res/drawable/ic_filter_list_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_filter_list_white_24dp.xml
rename to app/src/main/res/drawable/ic_filter_list_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_home_white_24dp.xml b/app/src/main/res/drawable/ic_home_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_home_white_24dp.xml
rename to app/src/main/res/drawable/ic_home_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_image_white_128dp.xml b/app/src/main/res/drawable/ic_image_white_128dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_image_white_128dp.xml
rename to app/src/main/res/drawable/ic_image_white_128dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_info_outline_black_24dp.xml
rename to app/src/main/res/drawable/ic_info_outline_black_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_info_outline_white_24dp.xml b/app/src/main/res/drawable/ic_info_outline_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_info_outline_white_24dp.xml
rename to app/src/main/res/drawable/ic_info_outline_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml
similarity index 52%
rename from hackertracker/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml
rename to app/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml
index 4ba163b8..a261ce20 100644
--- a/hackertracker/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml
+++ b/app/src/main/res/drawable/ic_keyboard_arrow_down_white_24dp.xml
@@ -1,9 +1,10 @@
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?menu_tint"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z" />
diff --git a/hackertracker/src/main/res/drawable/ic_link_white_24dp.xml b/app/src/main/res/drawable/ic_link_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_link_white_24dp.xml
rename to app/src/main/res/drawable/ic_link_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_map_white_24dp.xml b/app/src/main/res/drawable/ic_map_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_map_white_24dp.xml
rename to app/src/main/res/drawable/ic_map_white_24dp.xml
diff --git a/app/src/main/res/drawable/ic_menu_black_24dp.xml b/app/src/main/res/drawable/ic_menu_black_24dp.xml
new file mode 100644
index 00000000..8e95cbcb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_black_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/hackertracker/src/main/res/drawable/ic_people_white_24dp.xml b/app/src/main/res/drawable/ic_people_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_people_white_24dp.xml
rename to app/src/main/res/drawable/ic_people_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_schedule_white_24dp.xml b/app/src/main/res/drawable/ic_schedule_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_schedule_white_24dp.xml
rename to app/src/main/res/drawable/ic_schedule_white_24dp.xml
diff --git a/app/src/main/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_white_24dp.xml
new file mode 100644
index 00000000..febf4271
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search_white_24dp.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/hackertracker/src/main/res/drawable/ic_settings_white_24dp.xml b/app/src/main/res/drawable/ic_settings_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_settings_white_24dp.xml
rename to app/src/main/res/drawable/ic_settings_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_share_white_24dp.xml b/app/src/main/res/drawable/ic_share_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_share_white_24dp.xml
rename to app/src/main/res/drawable/ic_share_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_star_accent_24dp.xml b/app/src/main/res/drawable/ic_star_accent_24dp.xml
similarity index 82%
rename from hackertracker/src/main/res/drawable/ic_star_accent_24dp.xml
rename to app/src/main/res/drawable/ic_star_accent_24dp.xml
index 6e6af653..04a07ce8 100644
--- a/hackertracker/src/main/res/drawable/ic_star_accent_24dp.xml
+++ b/app/src/main/res/drawable/ic_star_accent_24dp.xml
@@ -1,4 +1,4 @@
-
diff --git a/hackertracker/src/main/res/drawable/ic_star_border_white_24dp.xml b/app/src/main/res/drawable/ic_star_border_white_24dp.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_star_border_white_24dp.xml
rename to app/src/main/res/drawable/ic_star_border_white_24dp.xml
diff --git a/hackertracker/src/main/res/drawable/ic_twitter.xml b/app/src/main/res/drawable/ic_twitter.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/ic_twitter.xml
rename to app/src/main/res/drawable/ic_twitter.xml
diff --git a/hackertracker/src/main/res/drawable/progress_bar.xml b/app/src/main/res/drawable/progress_bar.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/progress_bar.xml
rename to app/src/main/res/drawable/progress_bar.xml
diff --git a/hackertracker/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml
similarity index 100%
rename from hackertracker/src/main/res/drawable/splash_background.xml
rename to app/src/main/res/drawable/splash_background.xml
diff --git a/hackertracker/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/activity_main.xml
rename to app/src/main/res/layout/activity_main.xml
diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml
new file mode 100644
index 00000000..fc43323f
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_main.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/hackertracker/src/main/res/layout/bottom_sheet_generic.xml b/app/src/main/res/layout/bottom_sheet_generic.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/bottom_sheet_generic.xml
rename to app/src/main/res/layout/bottom_sheet_generic.xml
diff --git a/hackertracker/src/main/res/layout/bottom_sheet_review.xml b/app/src/main/res/layout/bottom_sheet_review.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/bottom_sheet_review.xml
rename to app/src/main/res/layout/bottom_sheet_review.xml
diff --git a/hackertracker/src/main/res/layout/empty_feed.xml b/app/src/main/res/layout/empty_feed.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/empty_feed.xml
rename to app/src/main/res/layout/empty_feed.xml
diff --git a/hackertracker/src/main/res/layout/empty_text.xml b/app/src/main/res/layout/empty_text.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/empty_text.xml
rename to app/src/main/res/layout/empty_text.xml
diff --git a/hackertracker/src/main/res/layout/fragment_event.xml b/app/src/main/res/layout/fragment_event.xml
similarity index 76%
rename from hackertracker/src/main/res/layout/fragment_event.xml
rename to app/src/main/res/layout/fragment_event.xml
index 2766915b..f3f94473 100644
--- a/hackertracker/src/main/res/layout/fragment_event.xml
+++ b/app/src/main/res/layout/fragment_event.xml
@@ -40,9 +40,10 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
+ android:layout_marginTop="0dp"
android:background="@android:color/transparent"
app:layout_collapseMode="pin"
- app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
+ app:navigationIcon="@drawable/ic_arrow_back_white_24dp" />
@@ -76,13 +77,21 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
+ android:padding="0dp" />
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
new file mode 100644
index 00000000..f4ad6c62
--- /dev/null
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_info.xml b/app/src/main/res/layout/fragment_info.xml
new file mode 100644
index 00000000..dd70ea5a
--- /dev/null
+++ b/app/src/main/res/layout/fragment_info.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_information.xml b/app/src/main/res/layout/fragment_information.xml
new file mode 100644
index 00000000..bda2a5c3
--- /dev/null
+++ b/app/src/main/res/layout/fragment_information.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/fragment_list.xml b/app/src/main/res/layout/fragment_list.xml
similarity index 91%
rename from hackertracker/src/main/res/layout/fragment_list.xml
rename to app/src/main/res/layout/fragment_list.xml
index fae67921..dc8919c7 100644
--- a/hackertracker/src/main/res/layout/fragment_list.xml
+++ b/app/src/main/res/layout/fragment_list.xml
@@ -8,4 +8,4 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="30dp"
- tools:listitem="@layout/row" />
\ No newline at end of file
+ tools:listitem="@layout/row_event" />
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/fragment_map.xml b/app/src/main/res/layout/fragment_map.xml
similarity index 75%
rename from hackertracker/src/main/res/layout/fragment_map.xml
rename to app/src/main/res/layout/fragment_map.xml
index 9c43f075..de97a3d7 100644
--- a/hackertracker/src/main/res/layout/fragment_map.xml
+++ b/app/src/main/res/layout/fragment_map.xml
@@ -1,19 +1,20 @@
+ android:layout_height="match_parent"
+ >
-
+ android:background="?android:colorBackground" />
+ android:background="?android:colorBackground">
+ android:layout_height="match_parent">
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
+ app:layout_constraintStart_toStartOf="parent" />
+ app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_recyclerview.xml b/app/src/main/res/layout/fragment_recyclerview.xml
new file mode 100644
index 00000000..8de4a295
--- /dev/null
+++ b/app/src/main/res/layout/fragment_recyclerview.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hackertracker/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/fragment_schedule.xml
similarity index 51%
rename from hackertracker/src/main/res/layout/app_bar_main.xml
rename to app/src/main/res/layout/fragment_schedule.xml
index 7b36b3c5..7fb791f1 100644
--- a/hackertracker/src/main/res/layout/app_bar_main.xml
+++ b/app/src/main/res/layout/fragment_schedule.xml
@@ -4,35 +4,59 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/background"
- android:fitsSystemWindows="true">
+ android:background="?android:colorBackground">
+ android:background="?android:colorBackground">
+ android:layout_height="wrap_content"
+ app:navigationIcon="@drawable/ic_menu_black_24dp"
+ app:title="@string/schedule" />
+
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+
+
+
+
+
+
+
+
+ app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
-
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
new file mode 100644
index 00000000..faa66dcb
--- /dev/null
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
new file mode 100644
index 00000000..f9fe9dac
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/fragment_speakers.xml b/app/src/main/res/layout/fragment_speakers.xml
similarity index 85%
rename from hackertracker/src/main/res/layout/fragment_speakers.xml
rename to app/src/main/res/layout/fragment_speakers.xml
index a8e357d0..f7d8c4b8 100644
--- a/hackertracker/src/main/res/layout/fragment_speakers.xml
+++ b/app/src/main/res/layout/fragment_speakers.xml
@@ -67,13 +67,25 @@
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
+ android:tint="?android:colorForeground"
app:srcCompat="@drawable/ic_twitter" />
-
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_article.xml b/app/src/main/res/layout/item_article.xml
new file mode 100644
index 00000000..c6a12943
--- /dev/null
+++ b/app/src/main/res/layout/item_article.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/item_header.xml b/app/src/main/res/layout/item_header.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/item_header.xml
rename to app/src/main/res/layout/item_header.xml
diff --git a/hackertracker/src/main/res/layout/item_type.xml b/app/src/main/res/layout/item_type.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/item_type.xml
rename to app/src/main/res/layout/item_type.xml
diff --git a/hackertracker/src/main/res/layout/item_type_header.xml b/app/src/main/res/layout/item_type_header.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/item_type_header.xml
rename to app/src/main/res/layout/item_type_header.xml
diff --git a/hackertracker/src/main/res/layout/item_upcoming.xml b/app/src/main/res/layout/item_upcoming.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/item_upcoming.xml
rename to app/src/main/res/layout/item_upcoming.xml
diff --git a/hackertracker/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
similarity index 77%
rename from hackertracker/src/main/res/layout/nav_header_main.xml
rename to app/src/main/res/layout/nav_header_main.xml
index 537bb956..5acef673 100644
--- a/hackertracker/src/main/res/layout/nav_header_main.xml
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -19,7 +19,8 @@
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitCenter"
- android:src="@drawable/skull" />
+ android:src="?footer_icon"
+ android:tint="?footer_tint" />
+
+
diff --git a/hackertracker/src/main/res/layout/row_event.xml b/app/src/main/res/layout/row_event.xml
similarity index 50%
rename from hackertracker/src/main/res/layout/row_event.xml
rename to app/src/main/res/layout/row_event.xml
index ba7b1e1d..e5589e6c 100644
--- a/hackertracker/src/main/res/layout/row_event.xml
+++ b/app/src/main/res/layout/row_event.xml
@@ -3,48 +3,16 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/background"
- android:orientation="horizontal">
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content">
@@ -54,44 +22,38 @@
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
app:layout_constraintEnd_toStartOf="@+id/star_bar"
- app:layout_constraintStart_toEndOf="@+id/category"
+ app:layout_constraintStart_toEndOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
- tools:text="Compelled Decryption - State of the Art in Doctrinal Perversions" />
-
+ tools:text="Compelled Decryption" />
-
-
+ tools:text="Track 1" />
+
+
+ app:layout_constraintStart_toEndOf="@+id/category_dot"
+ app:layout_constraintTop_toBottomOf="@+id/location"
+ tools:text="Event" />
+ tools:srcCompat="@drawable/ic_star_accent_24dp" />
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/row_faq.xml b/app/src/main/res/layout/row_faq.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/row_faq.xml
rename to app/src/main/res/layout/row_faq.xml
diff --git a/hackertracker/src/main/res/layout/row_footer.xml b/app/src/main/res/layout/row_footer.xml
similarity index 71%
rename from hackertracker/src/main/res/layout/row_footer.xml
rename to app/src/main/res/layout/row_footer.xml
index aab9815e..af36459d 100644
--- a/hackertracker/src/main/res/layout/row_footer.xml
+++ b/app/src/main/res/layout/row_footer.xml
@@ -1,10 +1,9 @@
+ android:src="?footer_icon"
+ android:tint="?footer_tint" />
diff --git a/hackertracker/src/main/res/layout/row_header.xml b/app/src/main/res/layout/row_header.xml
similarity index 65%
rename from hackertracker/src/main/res/layout/row_header.xml
rename to app/src/main/res/layout/row_header.xml
index c554bccc..8e370ffb 100644
--- a/hackertracker/src/main/res/layout/row_header.xml
+++ b/app/src/main/res/layout/row_header.xml
@@ -1,11 +1,9 @@
diff --git a/app/src/main/res/layout/row_header_time.xml b/app/src/main/res/layout/row_header_time.xml
new file mode 100644
index 00000000..5a9218dd
--- /dev/null
+++ b/app/src/main/res/layout/row_header_time.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/hackertracker/src/main/res/layout/row_info.xml b/app/src/main/res/layout/row_info.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/row_info.xml
rename to app/src/main/res/layout/row_info.xml
diff --git a/hackertracker/src/main/res/layout/row_nav.xml b/app/src/main/res/layout/row_nav.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/row_nav.xml
rename to app/src/main/res/layout/row_nav.xml
diff --git a/hackertracker/src/main/res/layout/row_nav_view.xml b/app/src/main/res/layout/row_nav_view.xml
similarity index 80%
rename from hackertracker/src/main/res/layout/row_nav_view.xml
rename to app/src/main/res/layout/row_nav_view.xml
index c7785567..16bd340e 100644
--- a/hackertracker/src/main/res/layout/row_nav_view.xml
+++ b/app/src/main/res/layout/row_nav_view.xml
@@ -6,10 +6,9 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
- android:background="@color/background"
- android:fitsSystemWindows="true"
+ app:itemTextColor="?android:colorForeground"
+ app:itemIconTint="?android:colorForeground"
app:headerLayout="@layout/nav_header_main"
- app:itemTextColor="@drawable/nav_text_color"
app:menu="@menu/activity_main_drawer"
tools:showIn="@layout/activity_main" />
diff --git a/app/src/main/res/layout/row_speaker.xml b/app/src/main/res/layout/row_speaker.xml
new file mode 100644
index 00000000..3ea39078
--- /dev/null
+++ b/app/src/main/res/layout/row_speaker.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/row_sub_header.xml b/app/src/main/res/layout/row_sub_header.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/row_sub_header.xml
rename to app/src/main/res/layout/row_sub_header.xml
diff --git a/hackertracker/src/main/res/layout/row_time_container.xml b/app/src/main/res/layout/row_time_container.xml
similarity index 99%
rename from hackertracker/src/main/res/layout/row_time_container.xml
rename to app/src/main/res/layout/row_time_container.xml
index c3bbd200..f3f47393 100644
--- a/hackertracker/src/main/res/layout/row_time_container.xml
+++ b/app/src/main/res/layout/row_time_container.xml
@@ -1,5 +1,4 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/select_dialog_item.xml b/app/src/main/res/layout/select_dialog_item.xml
similarity index 94%
rename from hackertracker/src/main/res/layout/select_dialog_item.xml
rename to app/src/main/res/layout/select_dialog_item.xml
index ed1374f4..a9f0ca77 100644
--- a/hackertracker/src/main/res/layout/select_dialog_item.xml
+++ b/app/src/main/res/layout/select_dialog_item.xml
@@ -7,9 +7,8 @@
android:ellipsize="marquee"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="16dp"
android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="@color/white"
android:textSize="16sp"
tools:text="@tools:sample/lorem" />
diff --git a/app/src/main/res/layout/view_code_of_conduct.xml b/app/src/main/res/layout/view_code_of_conduct.xml
new file mode 100644
index 00000000..b9624f98
--- /dev/null
+++ b/app/src/main/res/layout/view_code_of_conduct.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_day.xml b/app/src/main/res/layout/view_day.xml
new file mode 100644
index 00000000..4a75267d
--- /dev/null
+++ b/app/src/main/res/layout/view_day.xml
@@ -0,0 +1,22 @@
+
+
diff --git a/app/src/main/res/layout/view_day_selector.xml b/app/src/main/res/layout/view_day_selector.xml
new file mode 100644
index 00000000..1001671e
--- /dev/null
+++ b/app/src/main/res/layout/view_day_selector.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/view_empty.xml b/app/src/main/res/layout/view_empty.xml
similarity index 94%
rename from hackertracker/src/main/res/layout/view_empty.xml
rename to app/src/main/res/layout/view_empty.xml
index 84cefb69..1b82ec41 100644
--- a/hackertracker/src/main/res/layout/view_empty.xml
+++ b/app/src/main/res/layout/view_empty.xml
@@ -23,7 +23,8 @@
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/splash_background"
+ android:src="@drawable/skull_lg"
+ android:tint="?android:colorForeground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/right_guide"
app:layout_constraintStart_toStartOf="@id/left_guide"
@@ -37,7 +38,6 @@
android:layout_marginTop="24dp"
android:gravity="center"
android:text="@string/no_results_for"
- android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="@id/right_guide"
app:layout_constraintStart_toStartOf="@id/left_guide"
@@ -51,7 +51,6 @@
android:fontFamily="sans-serif-light"
android:gravity="center"
android:text="@string/no_results_message"
- android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="@id/right_guide"
app:layout_constraintStart_toStartOf="@id/left_guide"
diff --git a/hackertracker/src/main/res/layout/view_filter.xml b/app/src/main/res/layout/view_filter.xml
similarity index 100%
rename from hackertracker/src/main/res/layout/view_filter.xml
rename to app/src/main/res/layout/view_filter.xml
diff --git a/app/src/main/res/layout/view_help_line.xml b/app/src/main/res/layout/view_help_line.xml
new file mode 100644
index 00000000..ba0f92a5
--- /dev/null
+++ b/app/src/main/res/layout/view_help_line.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/layout/view_settings_action.xml b/app/src/main/res/layout/view_settings_action.xml
similarity index 97%
rename from hackertracker/src/main/res/layout/view_settings_action.xml
rename to app/src/main/res/layout/view_settings_action.xml
index 020e6edf..23eea49d 100644
--- a/hackertracker/src/main/res/layout/view_settings_action.xml
+++ b/app/src/main/res/layout/view_settings_action.xml
@@ -16,7 +16,6 @@
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
- android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/hackertracker/src/main/res/layout/view_settings_switch.xml b/app/src/main/res/layout/view_settings_switch.xml
similarity index 97%
rename from hackertracker/src/main/res/layout/view_settings_switch.xml
rename to app/src/main/res/layout/view_settings_switch.xml
index 2ba9f0da..8f8740ef 100644
--- a/hackertracker/src/main/res/layout/view_settings_switch.xml
+++ b/app/src/main/res/layout/view_settings_switch.xml
@@ -28,7 +28,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:textColor="@color/white"
tools:text="Send anonymous usage statistics" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
similarity index 71%
rename from hackertracker/src/main/res/menu/activity_main_drawer.xml
rename to app/src/main/res/menu/activity_main_drawer.xml
index 6cc26f68..b80ee38c 100644
--- a/hackertracker/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -5,11 +5,11 @@
android:checkableBehavior="single">
@@ -23,16 +23,6 @@
android:icon="@drawable/ic_info_outline_white_24dp"
android:title="@string/information" />
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/schedule.xml b/app/src/main/res/menu/schedule.xml
new file mode 100644
index 00000000..9c2c4771
--- /dev/null
+++ b/app/src/main/res/menu/schedule.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/hackertracker/src/main/res/menu/search_menu.xml b/app/src/main/res/menu/search_menu.xml
similarity index 100%
rename from hackertracker/src/main/res/menu/search_menu.xml
rename to app/src/main/res/menu/search_menu.xml
diff --git a/hackertracker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from hackertracker/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/hackertracker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from hackertracker/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/hackertracker/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-hdpi/ic_launcher.png
rename to app/src/main/res/mipmap-hdpi/ic_launcher.png
diff --git a/hackertracker/src/main/res/mipmap-hdpi/ic_launcher_background.png b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-hdpi/ic_launcher_background.png
rename to app/src/main/res/mipmap-hdpi/ic_launcher_background.png
diff --git a/hackertracker/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
rename to app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
diff --git a/hackertracker/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-hdpi/ic_launcher_round.png
rename to app/src/main/res/mipmap-hdpi/ic_launcher_round.png
diff --git a/hackertracker/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-mdpi/ic_launcher.png
rename to app/src/main/res/mipmap-mdpi/ic_launcher.png
diff --git a/hackertracker/src/main/res/mipmap-mdpi/ic_launcher_background.png b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-mdpi/ic_launcher_background.png
rename to app/src/main/res/mipmap-mdpi/ic_launcher_background.png
diff --git a/hackertracker/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
rename to app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
diff --git a/hackertracker/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-mdpi/ic_launcher_round.png
rename to app/src/main/res/mipmap-mdpi/ic_launcher_round.png
diff --git a/hackertracker/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to app/src/main/res/mipmap-xhdpi/ic_launcher.png
diff --git a/hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_background.png
rename to app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
diff --git a/hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
rename to app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
diff --git a/hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xhdpi/ic_launcher_round.png
rename to app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
diff --git a/hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher.png
diff --git a/hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
diff --git a/hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
diff --git a/hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
diff --git a/hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher.png
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
diff --git a/hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
diff --git a/hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
diff --git a/hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
similarity index 100%
rename from hackertracker/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
diff --git a/app/src/main/res/raw/digirootonly.cer b/app/src/main/res/raw/digirootonly.cer
new file mode 100644
index 00000000..2f1e5523
Binary files /dev/null and b/app/src/main/res/raw/digirootonly.cer differ
diff --git a/hackertracker/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
similarity index 92%
rename from hackertracker/src/main/res/values-v21/styles.xml
rename to app/src/main/res/values-v21/styles.xml
index fbc3bffb..8468209b 100644
--- a/hackertracker/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -7,7 +7,7 @@
- true
- @android:transition/move
- @android:transition/move
- - true
+
diff --git a/hackertracker/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
similarity index 100%
rename from hackertracker/src/main/res/values-w820dp/dimens.xml
rename to app/src/main/res/values-w820dp/dimens.xml
diff --git a/hackertracker/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
similarity index 100%
rename from hackertracker/src/main/res/values/array.xml
rename to app/src/main/res/values/array.xml
diff --git a/hackertracker/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
similarity index 61%
rename from hackertracker/src/main/res/values/attrs.xml
rename to app/src/main/res/values/attrs.xml
index f28b7750..78caabe8 100644
--- a/hackertracker/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -1,5 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hackertracker/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
similarity index 88%
rename from hackertracker/src/main/res/values/colors.xml
rename to app/src/main/res/values/colors.xml
index 05c03961..6e319956 100644
--- a/hackertracker/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -43,6 +43,15 @@
#fc853f
#50d7e8
+ #653fff
+
+ #0F0
+ #F00
+ #00F
+
+ #FF69B4
+ #ffb6db
+
+ 85dp
+ 4dp
+ 16dp
diff --git a/hackertracker/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
similarity index 64%
rename from hackertracker/src/main/res/values/strings.xml
rename to app/src/main/res/values/strings.xml
index d89c1478..37650850 100644
--- a/hackertracker/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -48,6 +48,7 @@
DEMO
TOOL
EXPLOIT
+ %1$s, %2$s
%1$s, %2$s - %3$s
@@ -84,26 +85,32 @@
- Quick Connect to DEF CON WiFi
- Quickly connect to DEF CON\'s secure WiFi.
-
- Install DEF CON Certificate
- Select \'Wi-Fi\' for credential use. Your device will need a PIN to install the certificate.
- Install
-
- Connect to WiFi
- Save the DEF CON WiFi to your device.
- Connect
-
- Or click here to do it yourself!
- Link
+ WIFI
+ Quickly connect to DEF CON\'s WiFi using Hacker Tracker.\n\nIf you run into any issues, you can try doing it yourself: wifireg.defcon.org/android.php
+ Your Android version is too low to setup. You can try doing it yourself: wifireg.defcon.org/android.php
+ Save network
+
Website
- Choose theme
+
Version %1$s
- Change conference
+ Choose theme
+ Choose conference
+ Events in local timezone (%1$s)
+
+
+
+ The DEF CON Support Hotline
+ You can reach DEF CON staff during normal hours of operation to anonymously report any behaviour violating our code of conduct or to find an empathic ear by calling the Hotline. Trained community volunteers will be standing by to help any attendees.\n\nYou can still report issues by going to any Info Booth or talking to any SOC Goon, but sometimes you may not want to be walking around in person with a problem, and so we have added a phone option. When you call the Hotline you will reach DEF CON community Goons trained to help in these areas.
+ Call +1 (725) 222–0934
+
+
+ Note: The hotline is not for general DEF CON questions, if you have those, please see the nearest Info Booth. \n\nWould you still like to call?
+ Call
+
+ Code of Conduct
diff --git a/hackertracker/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
similarity index 58%
rename from hackertracker/src/main/res/values/styles.xml
rename to app/src/main/res/values/styles.xml
index 77076d41..8e17ae46 100644
--- a/hackertracker/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -14,27 +14,24 @@
- @style/PreferenceFragmentList
-
-
+
+ - dark
+ - @drawable/skull
+ - @color/white
+
+ - @color/white
+ - @color/dark_background
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hackertracker/src/main/res/xml/searchable.xml b/app/src/main/res/xml/searchable.xml
similarity index 100%
rename from hackertracker/src/main/res/xml/searchable.xml
rename to app/src/main/res/xml/searchable.xml
diff --git a/hackertracker/src/test/java/com/shortstack/hackertracker/Utils.kt b/app/src/test/java/com/shortstack/hackertracker/Utils.kt
similarity index 77%
rename from hackertracker/src/test/java/com/shortstack/hackertracker/Utils.kt
rename to app/src/test/java/com/shortstack/hackertracker/Utils.kt
index 4efb89e7..9e84d7c6 100644
--- a/hackertracker/src/test/java/com/shortstack/hackertracker/Utils.kt
+++ b/app/src/test/java/com/shortstack/hackertracker/Utils.kt
@@ -1,5 +1,6 @@
package com.shortstack.hackertracker
+import com.google.firebase.Timestamp
import com.shortstack.hackertracker.utilities.MyClock
import com.shortstack.hackertracker.utilities.now
import io.mockk.every
@@ -17,3 +18,7 @@ fun setCurrentClock(date: String) {
fun parse(date: String): Date {
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").parse(date)
}
+
+fun fromString(date: String): Timestamp {
+ return Timestamp(SimpleDateFormat("yyyy-MM-dd").parse(date))
+}
diff --git a/app/src/test/java/com/shortstack/hackertracker/database/DatabaseManagerTest.kt b/app/src/test/java/com/shortstack/hackertracker/database/DatabaseManagerTest.kt
new file mode 100644
index 00000000..e092e8ef
--- /dev/null
+++ b/app/src/test/java/com/shortstack/hackertracker/database/DatabaseManagerTest.kt
@@ -0,0 +1,92 @@
+package com.shortstack.hackertracker.database
+
+import com.shortstack.hackertracker.fromString
+import com.shortstack.hackertracker.models.firebase.FirebaseConference
+import com.shortstack.hackertracker.models.local.Conference
+import com.shortstack.hackertracker.setCurrentClock
+import com.shortstack.hackertracker.toConference
+import org.junit.Assert.*
+import org.junit.Test
+
+class DatabaseManagerTest {
+
+ private val conferences: List = listOf(
+ FirebaseConference(id = 1, code = "abc", start_timestamp = fromString("2019-01-01"), end_timestamp = fromString("2019-01-01")),
+ FirebaseConference(id = 2, code = "123", start_timestamp = fromString("2019-03-02"), end_timestamp = fromString("2019-03-03")),
+ FirebaseConference(id = 3, code = "456", start_timestamp = fromString("2019-05-02"), end_timestamp = fromString("2019-05-03"))
+ ).map { it.toConference() }
+
+ @Test
+ fun `get newest conference` (){
+ setCurrentClock("2019-01-02T11:00:00.000-0000")
+
+ val result = DatabaseManager.getNextConference(-1, conferences)
+
+ assertEquals("123", result?.code)
+ }
+
+ @Test
+ fun `get preferred conference` (){
+ setCurrentClock("2019-01-01T11:00:00.000-0000")
+
+ val result = DatabaseManager.getNextConference(2, conferences)
+
+ assertEquals("123", result?.code)
+ }
+
+ @Test
+ fun `get defcon conference` (){
+ setCurrentClock("2019-01-01T11:00:00.000-0000")
+
+ val defcon = FirebaseConference(
+ id = 4,
+ code = "DEFCON27",
+ start_timestamp = fromString("2019-05-02"),
+ end_timestamp = fromString("2019-05-03")
+ ).toConference()
+
+ val conferences = conferences + defcon
+
+ val result = DatabaseManager.getNextConference(-1, conferences)
+
+ assertEquals("DEFCON27", result?.code)
+ }
+
+ @Test
+ fun `skip defcon conference when finished` (){
+ setCurrentClock("2019-03-03T11:00:00.000-0000")
+
+ val defcon = FirebaseConference(
+ id = 4,
+ code = "DEFCON27",
+ start_timestamp = fromString("2019-02-02"),
+ end_timestamp = fromString("2019-02-03")
+ ).toConference()
+
+ val conferences = conferences + defcon
+
+ val result = DatabaseManager.getNextConference(-1, conferences)
+
+ assertEquals("456", result?.code)
+ }
+
+
+ @Test
+ fun `get newest conference when all finished` (){
+ setCurrentClock("2019-06-01T11:00:00.000-0000")
+
+ val result = DatabaseManager.getNextConference(-1, conferences)
+
+ assertEquals("456", result?.code)
+ }
+
+ @Test
+ fun `get newest conference when all finished with preference` (){
+ setCurrentClock("2019-06-01T11:00:00.000-0000")
+
+ val result = DatabaseManager.getNextConference(1, conferences)
+
+ assertEquals("456", result?.code)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/shortstack/hackertracker/database/ReminderManagerTest.kt b/app/src/test/java/com/shortstack/hackertracker/database/ReminderManagerTest.kt
new file mode 100644
index 00000000..4716c057
--- /dev/null
+++ b/app/src/test/java/com/shortstack/hackertracker/database/ReminderManagerTest.kt
@@ -0,0 +1,75 @@
+package com.shortstack.hackertracker.database
+
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import com.nhaarman.mockitokotlin2.*
+import com.shortstack.hackertracker.models.firebase.FirebaseEvent
+import com.shortstack.hackertracker.setCurrentClock
+import com.shortstack.hackertracker.toEvent
+import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+
+class ReminderManagerTest {
+
+ private val database = mock()
+ private val work = mock()
+
+ private val manager = ReminderManager(database, work)
+
+ @Before
+ fun setUp() {
+ setCurrentClock("2020-01-01T00:00:00.000-0000")
+ }
+
+ @Test
+ fun `get event for notification`() = runBlocking {
+ val event = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:20:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+
+ whenever(database.getEventById("con", 12)).thenReturn(event)
+
+ val result = manager.getEvent("con", 12)
+
+ assertEquals(event.id, result?.id)
+ }
+
+ @Test
+ fun `set reminder when in 20 minutes or more`() {
+ val event = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:20:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+
+ manager.setReminder(event)
+
+ verify(work).enqueue(any())
+ }
+
+ @Test
+ fun `don't set reminder if less than 20 minutes`() {
+ val event = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:05:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+
+ manager.setReminder(event)
+
+ verify(work, never()).enqueue(any())
+ }
+
+ @Test
+ fun `update reminder when event is updated`() {
+ val event = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:20:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+ val updated = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:25:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+
+ manager.setReminder(event)
+ manager.setReminder(updated)
+
+ verify(work, times(2)).enqueue(any())
+ }
+
+ @Test
+ fun `clear reminder when unbookmarked`() {
+ val event = FirebaseEvent(12, "con", "event", begin = "2020-01-01T00:20:00.000-0000", end = "2020-01-01T00:20:00.000-0000").toEvent()
+
+ manager.cancel(event)
+
+ verify(work, never()).enqueue(any())
+ verify(work).cancelAllWorkByTag("reminder_12")
+ }
+}
\ No newline at end of file
diff --git a/hackertracker/src/test/java/com/shortstack/hackertracker/models/EventTest.kt b/app/src/test/java/com/shortstack/hackertracker/models/EventTest.kt
similarity index 100%
rename from hackertracker/src/test/java/com/shortstack/hackertracker/models/EventTest.kt
rename to app/src/test/java/com/shortstack/hackertracker/models/EventTest.kt
diff --git a/hackertracker/src/test/java/com/shortstack/hackertracker/models/VendorTest.kt b/app/src/test/java/com/shortstack/hackertracker/models/VendorTest.kt
similarity index 100%
rename from hackertracker/src/test/java/com/shortstack/hackertracker/models/VendorTest.kt
rename to app/src/test/java/com/shortstack/hackertracker/models/VendorTest.kt
diff --git a/hackertracker/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt b/app/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt
similarity index 90%
rename from hackertracker/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt
rename to app/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt
index 1c2d052d..05adffbd 100644
--- a/hackertracker/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt
+++ b/app/src/test/java/com/shortstack/hackertracker/utils/TimeUtilTest.kt
@@ -5,16 +5,12 @@ import android.text.format.DateFormat
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import com.shortstack.hackertracker.*
+import com.shortstack.hackertracker.utilities.TimeUtil
import io.mockk.every
import io.mockk.mockkStatic
-import io.mockk.staticMockk
-import io.mockk.use
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
-import java.text.SimpleDateFormat
-import java.util.*
-
class TimeUtilTest {
@@ -53,7 +49,7 @@ class TimeUtilTest {
val date = parse("2019-01-01T12:00:00.000-0000")
- val result = TimeUtil.getRelativeDateStamp(context, date)
+ val result = TimeUtil.getDateStamp(date)
assertEquals("Today", result)
}
@@ -63,7 +59,7 @@ class TimeUtilTest {
setCurrentClock("2019-01-01T12:00:00.000-0000")
val date = parse("2019-01-02T12:00:00.000-0000")
- val result = TimeUtil.getRelativeDateStamp(context, date)
+ val result = TimeUtil.getDateStamp(date)
assertEquals("Tomorrow", result)
}
diff --git a/build.gradle b/build.gradle
index 52a79f60..c4735544 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,28 +1,27 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.11'
+ ext.kotlin_version = '1.3.72'
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
jcenter()
- maven { url 'https://maven.google.com' }
+ maven { url "https://jitpack.io" }
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.android.tools.build:gradle:4.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'io.fabric.tools:gradle:1.25.4'
- classpath 'com.google.gms:google-services:4.0.1'
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath 'com.google.gms:google-services:4.3.3'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
}
}
allprojects {
repositories {
jcenter()
- maven { url 'https://maven.google.com' }
+ maven { url "https://jitpack.io" }
+ google()
}
}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..376270d7
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index eb1a4a2c..b07e02a0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Apr 29 23:40:54 PDT 2019
+#Mon Jun 15 17:58:36 PDT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/hackertracker/build.gradle b/hackertracker/build.gradle
deleted file mode 100644
index 0046528b..00000000
--- a/hackertracker/build.gradle
+++ /dev/null
@@ -1,144 +0,0 @@
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
-apply plugin: 'kotlin-kapt'
-apply plugin: 'maven'
-apply plugin: 'io.fabric'
-apply plugin: 'kotlin-kapt'
-apply plugin: 'com.google.gms.google-services'
-
-android {
- compileSdkVersion 28
- useLibrary 'org.apache.http.legacy'
- defaultConfig {
- applicationId "com.shortstack.hackertracker"
- minSdkVersion 16
- targetSdkVersion 28
- versionCode 173
- versionName "6.0.13"
-
- vectorDrawables.useSupportLibrary = true
-
- multiDexEnabled true
- }
- buildTypes {
- debug {
-// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
-// minifyEnabled true
- }
-
- release {
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
- minifyEnabled true
- useProguard true
- }
- }
- flavorDimensions "versionCode"
- productFlavors {
- defaultFlavor {
- dimension "versionCode"
- }
- }
- packagingOptions {
- exclude 'META-INF/LICENSE.txt'
- }
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- }
- kapt {
- generateStubs = true
- }
-}
-
-androidExtensions {
- experimental = true
-}
-
-configurations {
- // compile.exclude group: "org.apache.httpcomponents", module: "httpclient"
-}
-
-dependencies {
-
- implementation fileTree(include: ['*.jar'], dir: 'libs')
-
- // Kotlin
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
-
- // Support Libraries
- implementation 'com.google.android.material:material:1.0.0-beta01'
- implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
- implementation 'androidx.recyclerview:recyclerview:1.0.0-beta01'
- implementation 'androidx.cardview:cardview:1.0.0-beta01'
- implementation 'androidx.preference:preference:1.0.0-beta01'
-
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
-
- // RxJava
- implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
- implementation 'io.reactivex.rxjava2:rxjava:2.1.3'
-
- // Koin
- implementation 'org.koin:koin-android:1.0.2'
- implementation 'org.koin:koin-androidx-scope:1.0.2'
- implementation 'org.koin:koin-androidx-viewmodel:1.0.2'
-
- // Arch
- implementation "androidx.lifecycle:lifecycle-extensions:2.0.0-beta01"
- implementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0-beta01"
- implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha04"
- implementation "android.arch.work:work-firebase:1.0.0-alpha04"
-
- // Retrofit
- implementation 'com.squareup.retrofit2:retrofit:2.3.0'
- implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
- implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
- implementation 'com.squareup.okhttp3:okhttp:3.10.0'
- implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
-
- // Crash
- implementation('com.crashlytics.sdk.android:crashlytics:2.6.5@aar') {
- transitive = true
- }
-
- // Pretty Logger
- implementation 'com.orhanobut:logger:1.15'
- // PDF Viewer
- implementation 'com.joanzapata.pdfview:android-pdfview:1.0.4@aar'
- // Reviews
- implementation 'com.github.stkent:amplify:2.2.0'
- // Firebase Job Dispatcher
- implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
-
-
- implementation "com.hendraanggrian.material:collapsingtoolbarlayout-subtitle:1.0.0-beta01"
-
- // Firebase
- implementation 'com.google.firebase:firebase-database:17.0.0'
- implementation 'com.google.firebase:firebase-firestore:19.0.0'
- implementation 'com.google.firebase:firebase-storage:17.0.0'
- implementation 'com.google.firebase:firebase-auth:17.0.0'
- implementation 'com.google.firebase:firebase-messaging:18.0.0'
- implementation 'com.google.firebase:firebase-config:17.0.0'
-
-
-
-
- implementation 'com.android.support:multidex:1.0.3'
-
- testImplementation "junit:junit:4.12"
- testImplementation "io.mockk:mockk:1.9.3.kotlin12"
- testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
- testImplementation 'org.mockito:mockito-inline:2.13.0'
- testImplementation 'org.koin:koin-test:2.0.0-beta-4'
-
-
-}
-
-repositories {
- mavenCentral()
- maven { url "https://jitpack.io" }
- maven { url 'https://maven.fabric.io/public' }
- maven { url 'https://maven.google.com' }
-}
diff --git a/hackertracker/hackertracker-hackertracker.iml b/hackertracker/hackertracker-hackertracker.iml
deleted file mode 100644
index 95ca6f38..00000000
--- a/hackertracker/hackertracker-hackertracker.iml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/hackertracker/proguard-rules.txt b/hackertracker/proguard-rules.txt
deleted file mode 100644
index aa7cb7ec..00000000
--- a/hackertracker/proguard-rules.txt
+++ /dev/null
@@ -1,102 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /Users/whitneychampion/Documents/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the ProGuard
-# include property in project.properties.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-######### KEEP ANDROID SUPPORT V7 AND DESIGN
-#-keep class android.support.** { *; }
-#-keep interface android.support.** { *; }
-
-# Retrofit 2.X
-## https://square.github.io/retrofit/ ##
-# Retain generic types information for use by reflection by converters and adapters.
--keepattributes Signature
-
-# Retain service method parameters when optimizing.
--keepclassmembers,allowshrinking,allowobfuscation interface * {
- @retrofit2.http.* ;
-}
-
-# Ignore annotation used for build tooling.
--dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-
-# Ignore JSR 305 annotations for embedding nullability information.
--dontwarn javax.annotation.**
-
--dontwarn okhttp3.**
--dontwarn okio.**
--dontwarn javax.annotation.**
--dontwarn org.conscrypt.**
-# A resource is loaded with a relative path so the package of this class must be preserved.
--keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-
-# Preserve annotations, line numbers, and source file names
--keepattributes *Annotation*
--keepattributes SourceFile,LineNumberTable
--keep public class * extends java.lang.Exception
--keep class com.crashlytics.** { *; }
--dontwarn com.crashlytics.**
-
-# Retrofit
--dontwarn retrofit.**
--keep class retrofit.** { *; }
--keep class package.with.model.classes.** { *; }
--keepclassmembernames interface * {
- @retrofit.http.* ;
-}
--keep class com.squareup.okhttp3.** {
-*;
-}
-
--dontwarn org.codehaus.mojo.**
--keep class org.codehaus.mojo.** { *; }
-
-
--keepattributes Signature
--keepattributes Exceptions
-
--keep class com.shortstack.hackertracker.network.** { *; }
--keep class com.shortstack.hackertracker.models.** { *; }
-
--dontwarn com.shortstack.hackertracker.views.**
-
--keep class com.shortstack.hackertracker.ui.home.HomeFragment { *; }
--keep class com.shortstack.hackertracker.ui.schedule.ScheduleFragment { *; }
--keep class com.shortstack.hackertracker.ui.maps.MapsFragment { *; }
--keep class com.shortstack.hackertracker.ui.information.InformationFragment { *; }
--keep class com.shortstack.hackertracker.ui.vendors.VendorsFragment { *; }
--keep class com.shortstack.hackertracker.ui.SearchFragment { *; }
--keep class com.shortstack.hackertracker.ui.settings.SettingsFragment { *; }
-
--keep class android.support.v7.widget.SearchView { *; }
-
-
-
-# Parceler configuration
--keep interface org.parceler.Parcel
--keep @org.parceler.Parcel class * { *; }
--keep class **$$Parcelable { *; }
--keep class org.parceler.Parceler$$Parcels
-
-
-
-
-# Eventbus
--keepattributes *Annotation*
--keepclassmembers class ** {
- @com.squareup.otto.Subscribe public *;
- @com.squareup.otto.Produce public *;
-}
diff --git a/hackertracker/release/hackertracker.aab b/hackertracker/release/hackertracker.aab
deleted file mode 100644
index 3b638a1c..00000000
Binary files a/hackertracker/release/hackertracker.aab and /dev/null differ
diff --git a/hackertracker/src/main/assets/digicertca.cer b/hackertracker/src/main/assets/digicertca.cer
deleted file mode 100644
index 8f3aea9d..00000000
--- a/hackertracker/src/main/assets/digicertca.cer
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
-b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
-CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
-nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
-43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
-T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
-gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
-BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
-TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
-DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
-hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
-06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
-PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
-YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
-CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/App.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/App.kt
deleted file mode 100644
index fad90b3f..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/App.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.shortstack.hackertracker
-
-import androidx.appcompat.app.AppCompatDelegate
-import androidx.multidex.MultiDexApplication
-import com.crashlytics.android.Crashlytics
-import com.crashlytics.android.answers.Answers
-import com.crashlytics.android.core.CrashlyticsCore
-import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector
-import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector
-import com.github.stkent.amplify.tracking.Amplify
-import com.orhanobut.logger.Logger
-import com.shortstack.hackertracker.di.appModule
-import io.fabric.sdk.android.Fabric
-import org.koin.android.ext.android.startKoin
-
-
-class App : MultiDexApplication() {
-
- companion object {
- val isDeveloper = BuildConfig.DEBUG
- }
-
- override fun onCreate() {
- super.onCreate()
-
- AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
-
- startKoin(this, listOf(appModule))
-
- initFabric()
- initLogger()
- initFeedback()
-
- }
-
- private fun initFeedback() {
- Amplify.initSharedInstance(this)
- .setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
- .setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(Constants.FEEDBACK_EMAIL))
- .applyAllDefaultRules()
- .setLastUpdateTimeCooldownDays(1)
- }
-
- private fun initLogger() {
- Logger.init().methodCount(1).hideThreadInfo()
- }
-
- private fun initFabric() {
- val crashlytics = Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()).build()
- Fabric.with(this, crashlytics, Answers())
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/MyFirebaseMessagingService.java b/hackertracker/src/main/java/com/shortstack/hackertracker/MyFirebaseMessagingService.java
deleted file mode 100644
index cddbb7a6..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/MyFirebaseMessagingService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.shortstack.hackertracker;
-
-import com.google.firebase.messaging.FirebaseMessagingService;
-import com.google.firebase.messaging.RemoteMessage;
-import com.orhanobut.logger.Logger;
-
-public class MyFirebaseMessagingService extends FirebaseMessagingService {
-
- @Override
- public void onMessageReceived(RemoteMessage remoteMessage) {
- super.onMessageReceived(remoteMessage);
- Logger.d("onMessageReceived");
- }
-
- @Override
- public void onDeletedMessages() {
- super.onDeletedMessages();
- Logger.d("onDeletedMessages");
- }
-
- @Override
- public void onMessageSent(String s) {
- super.onMessageSent(s);
- Logger.d("onMessageSent");
- }
-
- @Override
- public void onSendError(String s, Exception e) {
- super.onSendError(s, e);
- Logger.d("onSendError");
- }
-
- @Override
- public void onNewToken(String s) {
- super.onNewToken(s);
- Logger.d("onNewToken");
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt
deleted file mode 100644
index 95f6b8f7..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/database/DatabaseManager.kt
+++ /dev/null
@@ -1,555 +0,0 @@
-package com.shortstack.hackertracker.database
-
-import android.util.Log
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.WorkManager
-import androidx.work.toWorkData
-import com.google.android.gms.tasks.OnCompleteListener
-import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.auth.FirebaseUser
-import com.google.firebase.firestore.FirebaseFirestore
-import com.google.firebase.firestore.FirebaseFirestoreSettings
-import com.google.firebase.firestore.Query
-import com.google.firebase.iid.FirebaseInstanceId
-import com.google.firebase.storage.FirebaseStorage
-import com.orhanobut.logger.Logger
-import com.shortstack.hackertracker.*
-import com.shortstack.hackertracker.models.firebase.*
-import com.shortstack.hackertracker.models.local.*
-import com.shortstack.hackertracker.network.task.ReminderWorker
-import com.shortstack.hackertracker.utilities.MyClock
-import com.shortstack.hackertracker.utilities.Storage
-import com.shortstack.hackertracker.utilities.now
-import io.reactivex.Single
-import java.io.File
-import java.util.concurrent.TimeUnit
-
-
-class DatabaseManager(private val preferences: Storage) {
-
- companion object {
- private const val CONFERENCES = "conferences"
-
- private const val USERS = "users"
- private const val BOOKMARKS = "bookmarks"
-
- private const val EVENTS = "events"
- private const val TYPES = "types"
- private const val FAQS = "faqs"
- private const val VENDORS = "vendors"
- private const val SPEAKERS = "speakers"
- private const val LOCATIONS = "locations"
- private const val ARTICLES = "articles"
-
- fun getNextConference(preferred: Int, conferences: List): Conference? {
- if (preferred != -1) {
- val pref = conferences.find { it.id == preferred && !it.hasFinished }
- if (pref != null) return pref
- }
-
- val list = conferences.sortedBy { it.startDate }
-
- val defcon = list.find { it.code == "DEFCON27" }
- if (defcon?.hasFinished == false) {
- return defcon
- }
-
- return list.firstOrNull { !it.hasFinished }
- ?: conferences.lastOrNull()
- }
- }
-
- private val code
- get() = conference.value?.code ?: "DEFCON27"
-
- private val firestore = FirebaseFirestore.getInstance()
- private val storage = FirebaseStorage.getInstance()
- private val auth = FirebaseAuth.getInstance()
-
-
- val conference = MutableLiveData()
- val conferences = MutableLiveData>()
-
- private var user: FirebaseUser? = null
-
- init {
- if (!BuildConfig.DEBUG) {
- val settings = FirebaseFirestoreSettings.Builder()
- .setPersistenceEnabled(true)
- .build()
-
- firestore.firestoreSettings = settings
- }
-
- auth.signInAnonymously().addOnCompleteListener {
- if (it.isSuccessful) {
- user = it.result?.user ?: return@addOnCompleteListener
- }
-
- firestore.collection(CONFERENCES)
- .get()
- .addOnCompleteListener {
- if (it.isSuccessful) {
- val list = it.result?.toObjects(FirebaseConference::class.java)
- ?.filter { !it.hidden || App.isDeveloper }
- ?.map { it.toConference() }
- ?.sortedBy { it.startDate }
-
- ?: emptyList()
-
-
- val con = getNextConference(preferences.preferredConference, list)
- conference.postValue(con)
- conferences.postValue(list)
-
- if (con != null)
- getFCMToken(con)
- }
- }
- }
- }
-
-
- private fun getFCMToken(conference: Conference) {
- FirebaseInstanceId.getInstance().instanceId
- .addOnCompleteListener(OnCompleteListener { task ->
- if (!task.isSuccessful) {
- Logger.e(task.exception, "Could not get token.")
- return@OnCompleteListener
- }
-
- // Get new Instance ID token
- val token = task.result?.token
- Logger.d("Obtained token: $token")
- updateFirebaseMessagingToken(conference, token)
- })
- }
-
- fun changeConference(id: Int) {
- preferences.preferredConference = id
-
- val current = conference.value
-
- if (current != null) {
- current.isSelected = false
- }
-
- firestore.collection(CONFERENCES)
- .whereEqualTo("id", id)
- .get()
- .addOnCompleteListener {
- if (it.isSuccessful) {
- val selected = it.result?.toObjects(FirebaseConference::class.java)?.firstOrNull()?.toConference()
- conference.postValue(selected)
- }
- }
- }
-
- fun getConferences(): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val cons = snapshot?.toObjects(FirebaseConference::class.java)
- ?.filter { !it.hidden || App.isDeveloper }
- ?.map { it.toConference() }
-
- mutableLiveData.postValue(cons)
- }
- }
-
- return mutableLiveData
- }
-
- fun getEvents(id: Conference): LiveData> {
- return getSchedule()
- }
-
- fun getSchedule(): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(EVENTS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val events = snapshot?.toObjects(FirebaseEvent::class.java)
- ?.filter { (!it.hidden || App.isDeveloper) && !it.type.filtered }
- ?.map { it.toEvent() }
-
- mutableLiveData.postValue(events)
-
- val id = user?.uid
- if (id != null) {
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(USERS)
- .document(id)
- .collection(BOOKMARKS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val bookmarks = snapshot?.toObjects(FirebaseBookmark::class.java)
-
- bookmarks?.forEach { bookmark ->
- events?.find { it.id.toString() == bookmark.id }?.isBookmarked = bookmark.value
- }
-
- mutableLiveData.postValue(events)
- }
- }
- }
- }
- }
-
- return mutableLiveData
- }
-
- fun getTypes(id: Conference): LiveData> {
- return getScheduleTypes()
- }
-
- fun getScheduleTypes(): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(TYPES)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val types = snapshot?.toObjects(FirebaseType::class.java)?.map { it.toType() }
- mutableLiveData.postValue(types)
-
- val id = user?.uid
- if (id != null) {
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(USERS)
- .document(id)
- .collection(TYPES)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val bookmarks = snapshot?.toObjects(FirebaseBookmark::class.java)
-
- types?.forEach { type ->
- type.isSelected = bookmarks?.find { type.id.toString() == it.id }?.value
- ?: false
- }
-
- mutableLiveData.postValue(types)
- }
- }
- }
- }
- }
-
- return mutableLiveData
- }
-
-
- fun getRecent(): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(EVENTS)
- .orderBy("updated_timestamp", Query.Direction.DESCENDING)
- .limit(3)
- .get()
- .addOnSuccessListener {
- val events = it.toObjects(FirebaseEvent::class.java)
- .filter { !it.hidden || App.isDeveloper }
- .map { it.toEvent() }
-
- mutableLiveData.postValue(events)
- }
-
- return mutableLiveData
- }
-
- fun getArticles(): LiveData> {
- val results = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(ARTICLES)
- .get()
- .addOnSuccessListener {
- val articles = it.toObjects(FirebaseArticle::class.java)
- .filter { !it.hidden || App.isDeveloper }
- .map { it.toArticle() }
-
- results.postValue(articles)
- }
-
- return results
- }
-
-
- fun getFAQ(code: String = this.code): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(FAQS)
- .get()
- .addOnSuccessListener {
- val faqs = it.toObjects(FirebaseFAQ::class.java)
- mutableLiveData.postValue(faqs)
- }
-
- return mutableLiveData
- }
-
- fun getLocations(): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(LOCATIONS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val list = snapshot?.toObjects(FirebaseLocation::class.java)?.map { it.toLocation() }
- mutableLiveData.postValue(list)
- }
- }
-
- return mutableLiveData
- }
-
- fun getVendors(conference: Conference): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(conference.code)
- .collection(VENDORS)
- .get()
- .addOnSuccessListener {
- val vendors = it.toObjects(FirebaseVendor::class.java)
- .filter { !it.hidden || App.isDeveloper }
- .map { it.toVendor() }
-
- mutableLiveData.postValue(vendors)
- }
- return mutableLiveData
- }
-
- fun getEventById(id: Int): Single {
- return Single.create { emitter ->
- firestore.collection(CONFERENCES)
- .document(id.toString())
- .get()
- .addOnSuccessListener {
- val event = it.toObject(FirebaseEvent::class.java)
- ?: return@addOnSuccessListener
- emitter.onSuccess(event.toEvent())
- }
- }
- }
-
- fun updateBookmark(event: Event) {
- val tag = "reminder_" + event.id
-
- if (event.isBookmarked) {
- val delay = event.start.time - MyClock().now().time - (1000 * 20 * 60)
-
- if (delay > 0) {
- val notify = OneTimeWorkRequestBuilder()
- .setInputData(mapOf(ReminderWorker.NOTIFICATION_ID to event.id).toWorkData())
- .setInitialDelay(delay, TimeUnit.MILLISECONDS)
- .addTag(tag)
- .build()
-
- WorkManager.getInstance()?.enqueue(notify)
- }
-
- } else {
- WorkManager.getInstance()?.cancelAllWorkByTag(tag)
- }
-
- val id = user?.uid ?: return
-
- val document = firestore.collection(CONFERENCES)
- .document(event.conference)
- .collection(USERS)
- .document(id)
- .collection(BOOKMARKS)
- .document(event.id.toString())
-
- if (event.isBookmarked) {
- document.set(mapOf("id" to event.id.toString(),
- "value" to true))
- } else {
- document.delete()
- }
- }
-
- fun updateTypeIsSelected(type: Type) {
- val id = user?.uid ?: return
-
- val document = firestore.collection(CONFERENCES)
- .document(type.conference)
- .collection(USERS)
- .document(id)
- .collection(TYPES)
- .document(type.id.toString())
-
- if (type.isSelected) {
- document.set(mapOf("id" to type.id.toString(),
- "value" to true))
- } else {
- document.delete()
- }
- }
-
- private fun updateFirebaseMessagingToken(conference: Conference?, token: String?) {
- val id = user?.uid
-
- if (conference == null || token == null || id == null) {
- Log.e("TAG", "Null, cannot update token.")
- return
- }
-
- val document = firestore.collection(CONFERENCES)
- .document(conference.code)
- .collection(USERS)
- .document(id)
-
-
- document.set(mapOf("token" to token))
- }
-
- fun clear() {
-
- }
-
- fun getSpeakers(): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(SPEAKERS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val speakers = snapshot?.toObjects(FirebaseSpeaker::class.java)
- ?.filter { !it.hidden || App.isDeveloper }
- ?.map { it.toSpeaker() }
- ?: emptyList()
-
- mutableLiveData.postValue(speakers)
- }
- }
-
- return mutableLiveData
- }
-
-
- fun getSpeakers(conference: Conference): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(conference.code)
- .collection(SPEAKERS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val speakers = snapshot?.toObjects(FirebaseSpeaker::class.java)
- ?.filter { !it.hidden || App.isDeveloper }
- ?.map { it.toSpeaker() }
- ?: emptyList()
-
- mutableLiveData.postValue(speakers)
- }
- }
-
- return mutableLiveData
- }
-
- fun getEventsForSpeaker(speaker: Speaker): LiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(code)
- .collection(EVENTS)
- .addSnapshotListener { snapshot, exception ->
- if (exception == null) {
- val events = snapshot?.toObjects(FirebaseEvent::class.java)
- val filtered = events?.filter { it.speakers.firstOrNull { it.id == speaker.id } != null }?.map { it.toEvent() }
- mutableLiveData.postValue(filtered)
- }
- }
-
- return mutableLiveData
- }
-
-
- fun getContests(conference: Conference): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(conference.code)
- .collection(EVENTS)
- .get()
- .addOnSuccessListener {
- val contests = it.toObjects(FirebaseEvent::class.java)
- .filter { (!it.hidden || App.isDeveloper) && it.type.name == "Contest" }
- .map { it.toEvent() }
-
- mutableLiveData.postValue(contests)
- }
-
- return mutableLiveData
- }
-
- fun getWorkshops(conference: Conference): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- firestore.collection(CONFERENCES)
- .document(conference.code)
- .collection(EVENTS)
- .get()
- .addOnSuccessListener {
- val workshops = it.toObjects(FirebaseEvent::class.java)
- .filter { (!it.hidden || App.isDeveloper) && it.type.name == "Workshop" }
- .map { it.toEvent() }
-
- mutableLiveData.postValue(workshops)
- }
-
- return mutableLiveData
- }
-
- fun getMaps(conference: Conference): MutableLiveData> {
- val mutableLiveData = MutableLiveData>()
-
- val list = ArrayList()
-
- val maps = conference.maps
- if (maps.isEmpty()) {
- mutableLiveData.postValue(emptyList())
- }
-
- maps.forEach {
-
- val temp = FirebaseConferenceMap(it.name, null)
- list.add(temp)
- mutableLiveData.postValue(list.toList())
-
- val map = storage.reference.child("/${conference.code}/${it.file}")
-
- val localFile = File.createTempFile("images", "pdf")
-
- map.getFile(localFile).addOnSuccessListener { task ->
- temp.file = localFile
-
- mutableLiveData.postValue(list.toList())
- }.addOnFailureListener {
- // Handle any errors
- }
- }
- return mutableLiveData
- }
-
-
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt
deleted file mode 100644
index e646d4d0..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/firebase/FirebaseConferenceMap.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.shortstack.hackertracker.models.firebase
-
-import java.io.File
-
-data class FirebaseConferenceMap(val title: String, var file: File?)
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Article.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Article.kt
deleted file mode 100644
index db2a37cc..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/models/local/Article.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.shortstack.hackertracker.models.local
-
-data class Article(val name: String, val text: String)
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt
deleted file mode 100644
index dae6ceb1..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/network/task/ReminderWorker.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.shortstack.hackertracker.network.task
-
-import androidx.work.Worker
-import com.shortstack.hackertracker.App
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.utilities.NotificationHelper
-import io.reactivex.disposables.Disposable
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class ReminderWorker : Worker(), KoinComponent {
-
- private val notifications: NotificationHelper by inject()
-
- private val database: DatabaseManager by inject()
-
- private var disposable: Disposable? = null
-
- override fun doWork(): Result {
- val id = inputData.getInt(NOTIFICATION_ID, -1)
-
- disposable = database.getEventById(id)
- .subscribe { event ->
- notifications.notifyStartingSoon(event)
- }
-
- return Result.SUCCESS
- }
-
- override fun onStopped(cancelled: Boolean) {
- disposable?.dispose()
- }
-
- companion object {
- const val NOTIFICATION_ID = "NOTIFICATION_ID"
- const val TAG = "TAG_REMINDER_"
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/GenericBottomSheet.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/GenericBottomSheet.kt
deleted file mode 100644
index 32361b20..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/GenericBottomSheet.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.shortstack.hackertracker.ui
-
-import android.app.Dialog
-import android.content.DialogInterface
-import android.content.Intent
-import android.net.Uri
-import android.text.TextUtils
-import android.view.View
-import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.utilities.MaterialAlert
-import kotlinx.android.synthetic.main.bottom_sheet_generic.view.*
-import kotlinx.android.synthetic.main.empty_text.view.*
-
-
-abstract class GenericBottomSheet : com.google.android.material.bottomsheet.BottomSheetDialogFragment() {
-
- override fun setupDialog(dialog : Dialog, style : Int) {
- super.setupDialog(dialog, style)
- val view = View.inflate(context, R.layout.bottom_sheet_generic, null)
- dialog.setContentView(view)
-
- view.title!!.text = getTitle()
-
- val isDescriptionEmpty = TextUtils.isEmpty(getDescription())
- view.empty!!.visibility = if (isDescriptionEmpty) View.VISIBLE else View.GONE
- view.description!!.text = getDescription()
-
- view.link!!.visibility = if (hasLink()) View.VISIBLE else View.GONE
- view.link.setOnClickListener { onLinkClick() }
- }
-
- protected abstract fun getLink() : String?
-
- protected abstract fun getTitle() : String
-
- protected abstract fun getDescription() : String
-
- protected abstract fun hasLink() : Boolean
-
- fun onLinkClick() {
- val context = context ?: return
-
- MaterialAlert.create(context)
- .setTitle(R.string.link_warning)
- .setMessage(String.format(context.getString(R.string.link_message), getLink()?.toLowerCase()))
- .setPositiveButton(R.string.open_link, DialogInterface.OnClickListener {
- _, _ ->
- val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(getLink()))
- context.startActivity(intent)
- }).setBasicNegativeButton()
- .show()
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivityViewModel.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivityViewModel.kt
deleted file mode 100644
index 780eed74..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/activities/MainActivityViewModel.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.shortstack.hackertracker.ui.activities
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Transformations
-import androidx.lifecycle.ViewModel
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.local.Conference
-import com.shortstack.hackertracker.models.local.Type
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class MainActivityViewModel : ViewModel(), KoinComponent {
-
- private val database: DatabaseManager by inject()
-
- val conference: LiveData
- get() = database.conference
-
- val conferences: LiveData>
- get() = database.getConferences()
-
- val types: LiveData>
- get() {
- return Transformations.switchMap(database.conference) { id ->
- if (id == null) {
- return@switchMap MutableLiveData>()
- }
- return@switchMap database.getScheduleTypes()
- }
- }
-
- fun changeConference(itemId: Int) {
- database.changeConference(itemId)
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsFragment.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsFragment.kt
deleted file mode 100644
index b968dc1d..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsFragment.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.shortstack.hackertracker.ui.contests
-
-import android.os.Bundle
-import android.view.View
-import androidx.lifecycle.Observer
-import com.shortstack.hackertracker.Resource
-import com.shortstack.hackertracker.models.Day
-import com.shortstack.hackertracker.models.Time
-import com.shortstack.hackertracker.models.local.Event
-import com.shortstack.hackertracker.ui.ListFragment
-
-class ContestsFragment : ListFragment() {
-
- companion object {
- fun newInstance() = ContestsFragment()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- getViewModel().contests.observe(this, Observer {
- if (it.data != null) {
- val elements = getFormattedElements(it.data)
- onResource(Resource.success(elements))
- } else {
- onResource(it)
- }
- })
- }
-
- private fun getFormattedElements(elements: List): ArrayList {
- val result = ArrayList()
-
-
- elements.groupBy { it.date }.toSortedMap().forEach {
- result.add(Day(it.key))
-
- it.value.groupBy { it.start }.toSortedMap().forEach {
- result.add(Time(it.key))
-
- if (it.value.isNotEmpty()) {
- val group = it.value.sortedWith(compareBy({ it.type.name }, { it.location.name }))
- result.addAll(group)
- }
- }
- }
-
- return result
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsViewModel.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsViewModel.kt
deleted file mode 100644
index ad3009ba..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/contests/ContestsViewModel.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.shortstack.hackertracker.ui.contests
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MediatorLiveData
-import androidx.lifecycle.Transformations
-import androidx.lifecycle.ViewModel
-import com.shortstack.hackertracker.Resource
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.local.Event
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class ContestsViewModel : ViewModel(), KoinComponent {
-
- private val database: DatabaseManager by inject()
-
- private val result = MediatorLiveData>>()
-
-
- val contests: LiveData>>
- get() {
- val conference = database.conference
- return Transformations.switchMap(conference) { id ->
- result.value = Resource.loading(null)
-
- if (id != null) {
- result.addSource(database.getContests(id)) {
- result.value = Resource.success(it)
- }
- } else {
- result.value = Resource.init(null)
- }
- return@switchMap result
- }
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt
deleted file mode 100644
index 526cd671..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeAdapter.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.shortstack.hackertracker.ui.home
-
-import android.view.ViewGroup
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
-import com.shortstack.hackertracker.models.local.Article
-import com.shortstack.hackertracker.models.local.Event
-import com.shortstack.hackertracker.ui.schedule.EventViewHolder
-
-class HomeAdapter : RecyclerView.Adapter() {
-
- companion object {
- private const val SKULL = 0
- private const val HEADER = 1
- private const val EVENT = 2
- private const val ARTICLE = 3
- }
-
- private val collection = ArrayList()
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return when (viewType) {
- SKULL -> SkullHeaderViewHolder.inflate(parent)
- HEADER -> HeaderViewHolder.inflate(parent)
- EVENT -> EventViewHolder.inflate(parent)
- ARTICLE -> ArticleViewHolder.inflate(parent)
- else -> throw IllegalStateException("Unknown viewType $viewType")
- }
- }
-
- override fun getItemViewType(position: Int): Int {
- if (position == 0)
- return SKULL
-
-
- return when (collection[position - 1]) {
- is Article -> ARTICLE
- is String -> HEADER
- else -> EVENT
- }
- }
-
- override fun getItemCount() = collection.size + 1
-
- override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- when (holder) {
- is EventViewHolder -> holder.render(collection[position - 1] as Event)
- is ArticleViewHolder -> holder.render(collection[position - 1] as Article)
- is HeaderViewHolder -> holder.render(collection[position - 1] as String)
- }
- }
-
- fun setElements(list: List) {
- val size = collection.size
- collection.clear()
- notifyItemRangeRemoved(1, size)
-
- collection.addAll(list)
- notifyItemRangeInserted(1, list.size)
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeViewModel.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeViewModel.kt
deleted file mode 100644
index ed956265..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/home/HomeViewModel.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.shortstack.hackertracker.ui.home
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MediatorLiveData
-import androidx.lifecycle.Transformations
-import androidx.lifecycle.ViewModel
-import com.orhanobut.logger.Logger
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.local.Article
-import com.shortstack.hackertracker.models.local.Event
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class HomeViewModel : ViewModel(), KoinComponent {
-
- private val database: DatabaseManager by inject()
-
- val results: LiveData>
-
- private val recent = database.getRecent()
- private val articles = database.getArticles()
-
- init {
- results = Transformations.switchMap(database.conference) {
- val results = MediatorLiveData>()
-
-
- results.addSource(recent) {
- val articles = articles.value ?: emptyList()
- setValue(results, articles, it)
- }
-
- results.addSource(articles) {
- val recent = recent.value ?: emptyList()
- setValue(results, it, recent)
- }
-
- return@switchMap results
- }
- }
-
- private fun setValue(results: MediatorLiveData>, articles: List, recent: List) {
- val list = ArrayList()
-
- list.add("Announcements")
- list.addAll(articles)
- list.add("Recent Updates")
- list.addAll(recent)
-
- results.postValue(list)
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationBottomSheet.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationBottomSheet.kt
deleted file mode 100644
index de475cb0..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationBottomSheet.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.shortstack.hackertracker.ui.information
-
-import android.os.Bundle
-import com.shortstack.hackertracker.ui.GenericBottomSheet
-
-import com.shortstack.hackertracker.models.Information
-
-class InformationBottomSheet : GenericBottomSheet() {
-
-
- override fun getLink(): String? {
- return null
- }
-
- override fun getTitle(): String {
- return arguments?.getString(ARG_TITLE) ?: ""
- }
-
- override fun getDescription(): String {
- return arguments?.getString(ARG_DESC) ?: ""
- }
-
- override fun hasLink(): Boolean {
- return false
- }
-
- companion object {
-
- private val ARG_TITLE = "TITLE"
- private val ARG_DESC = "DESCRIPTION"
-
- fun newInstance(title: String, description: String): InformationBottomSheet {
- val fragment = InformationBottomSheet()
-
- val bundle = Bundle()
- bundle.putString(ARG_TITLE, title)
- bundle.putString(ARG_DESC, description)
-
- fragment.arguments = bundle
-
- return fragment
- }
-
- fun newInstance(content: Information): InformationBottomSheet {
- return newInstance(content.title, content.description)
- }
- }
-
-
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt
deleted file mode 100644
index 17b577c5..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationFragment.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.shortstack.hackertracker.ui.information
-
-import android.os.Bundle
-import android.view.View
-import androidx.lifecycle.Observer
-import com.shortstack.hackertracker.models.firebase.FirebaseFAQ
-import com.shortstack.hackertracker.ui.ListFragment
-
-class InformationFragment : ListFragment() {
-
- companion object {
- fun newInstance() = InformationFragment()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- getViewModel().faq.observe(this, Observer {
- onResource(it)
- })
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewHolder.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewHolder.kt
deleted file mode 100644
index 4d1ac8ab..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewHolder.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.shortstack.hackertracker.ui.information
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.recyclerview.widget.RecyclerView
-import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.models.Information
-import kotlinx.android.synthetic.main.row_info.view.*
-
-class InformationViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
-
- companion object {
- fun inflate(parent: ViewGroup): InformationViewHolder {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.row_info, parent, false)
- return InformationViewHolder(view)
- }
- }
-
- fun render(information: Information) {
- view.header.text = information.title
-
- view.setOnClickListener {
- showInformationBottomSheet(information)
- }
- }
-
- private fun showInformationBottomSheet(information: Information) {
- val badges = InformationBottomSheet.newInstance(information)
- badges.show((view.context as AppCompatActivity).supportFragmentManager, badges.tag)
- }
-}
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewModel.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewModel.kt
deleted file mode 100644
index ccf5d559..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/information/InformationViewModel.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.shortstack.hackertracker.ui.information
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MediatorLiveData
-import androidx.lifecycle.Transformations
-import androidx.lifecycle.ViewModel
-import com.shortstack.hackertracker.Resource
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.firebase.FirebaseFAQ
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class InformationViewModel : ViewModel(), KoinComponent {
-
- private val database: DatabaseManager by inject()
-
- private val result = MediatorLiveData>>()
-
- val faq: LiveData>>
- get() {
- val conference = database.conference
- return Transformations.switchMap(conference) { id ->
- result.value = Resource.loading(null)
-
- if (id != null) {
- result.addSource(database.getFAQ(id.code)) {
- result.value = Resource.success(it)
- }
- } else {
- result.value = Resource.init(null)
- }
- return@switchMap result
- }
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsViewModel.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsViewModel.kt
deleted file mode 100644
index c5b32aa4..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/maps/MapsViewModel.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.shortstack.hackertracker.ui.maps
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Transformations
-import androidx.lifecycle.ViewModel
-import com.shortstack.hackertracker.database.DatabaseManager
-import com.shortstack.hackertracker.models.firebase.FirebaseConferenceMap
-import org.koin.standalone.KoinComponent
-import org.koin.standalone.inject
-
-class MapsViewModel : ViewModel(), KoinComponent {
-
- private val database: DatabaseManager by inject()
-
- val maps: LiveData>
- get() {
- return Transformations.switchMap(database.conference) { id ->
- if (id == null) {
- return@switchMap MutableLiveData>()
- }
- return@switchMap database.getMaps(id)
- }
- }
-}
\ No newline at end of file
diff --git a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt b/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt
deleted file mode 100644
index b2333cb5..00000000
--- a/hackertracker/src/main/java/com/shortstack/hackertracker/ui/schedule/ScheduleFragment.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.shortstack.hackertracker.ui.schedule
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
-import androidx.lifecycle.ViewModelProviders
-import com.shortstack.hackertracker.R
-import com.shortstack.hackertracker.Status
-import com.shortstack.hackertracker.models.Day
-import com.shortstack.hackertracker.models.Time
-import com.shortstack.hackertracker.models.local.Event
-import com.shortstack.hackertracker.ui.schedule.list.ScheduleAdapter
-import com.shortstack.hackertracker.utilities.TickTimer
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.Disposable
-import kotlinx.android.synthetic.main.fragment_schedule.*
-import kotlinx.android.synthetic.main.view_empty.view.*
-import org.koin.android.ext.android.inject
-
-class ScheduleFragment : Fragment() {
-
- private val adapter: ScheduleAdapter = ScheduleAdapter()
-
- private val timer: TickTimer by inject()
-
- private var disposable: Disposable? = null
-
- private var shouldScroll = true
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return inflater.inflate(R.layout.fragment_schedule, container, false) as ViewGroup
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- shouldScroll = true
- list.adapter = adapter
-
- val scheduleViewModel = ViewModelProviders.of(this).get(ScheduleViewModel::class.java)
- scheduleViewModel.schedule.observe(this, Observer {
- hideViews()
-
- if (it != null) {
- adapter.state = it.status
-
- when (it.status) {
- Status.SUCCESS -> {
- val list = adapter.setSchedule(it.data)
- if (adapter.isEmpty()) {
- showEmptyView()
- }
-
- scrollToCurrentPosition(list)
- }
- Status.ERROR -> {
- showErrorView(it.message)
- }
- Status.LOADING -> {
- adapter.clearAndNotify()
- showProgress()
- }
- Status.NOT_INITIALIZED -> {
- showEmptyView()
- }
- }
- }
- })
- }
-
- private fun scrollToCurrentPosition(data: ArrayList) {
- val manager = list.layoutManager ?: return
- val first = data.filterIsInstance().firstOrNull { !it.hasFinished } ?: return
-
- if (shouldScroll) {
- shouldScroll = false
- val index = getScrollIndex(data, first)
- manager.scrollToPosition(index)
- }
- }
-
- private fun getScrollIndex(data: ArrayList, first: Event): Int {
- val event = data.indexOf(first)
- val index = data.indexOf(data.subList(0, event).filterIsInstance