diff --git a/.circleci/config.yml b/.circleci/config.yml
index d4e59be1..f6646b52 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -7,11 +7,11 @@ references:
container_config: &container_config
docker:
- - image: circleci/android:api-28-alpha
+ - image: circleci/android:api-28
working_directory: *workspace_root
environment:
environment:
- JVM_OPTS: -Xmx3200m
+ _JAVA_OPTS: -Xmx2048m
attach_workspace: &attach_workspace
attach_workspace:
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5b281926..d670ef44 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -50,7 +50,16 @@
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
android:noHistory="true"
android:excludeFromRecents="true"
- android:theme="@style/WulkanowyTheme.TimetableWidgetAccount">
+ android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
+
+
+
+
+
@@ -72,6 +81,17 @@
android:resource="@xml/provider_widget_timetable" />
+
+
+
+
+
+
+
>
+
+ @Inject
+ lateinit var presenter: LuckyNumberWidgetConfigurePresenter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setResult(RESULT_CANCELED)
+ setContentView(R.layout.activity_widget_configure)
+
+ intent.extras.let {
+ presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
+ }
+ }
+
+ override fun initView() {
+ widgetConfigureRecycler.apply {
+ adapter = configureAdapter
+ layoutManager = SmoothScrollLinearLayoutManager(context)
+ }
+ configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) }
+ }
+
+ override fun updateData(data: List) {
+ configureAdapter.updateDataSet(data)
+ }
+
+ override fun updateLuckyNumberWidget(widgetId: Int) {
+ sendBroadcast(Intent(this, LuckyNumberWidgetProvider::class.java)
+ .apply {
+ action = ACTION_APPWIDGET_UPDATE
+ putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
+ })
+ }
+
+ override fun setSuccessResult(widgetId: Int) {
+ setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) })
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ Toast.makeText(this, text, Toast.LENGTH_LONG).show()
+ }
+
+ override fun finishView() {
+ finish()
+ }
+
+ override fun openLoginView() {
+ startActivity(LoginActivity.getStartIntent(this))
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ presenter.onDetachView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt
new file mode 100644
index 00000000..bba0974b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureItem.kt
@@ -0,0 +1,54 @@
+package io.github.wulkanowy.ui.modules.luckynumberwidget
+
+import android.annotation.SuppressLint
+import android.view.View
+import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
+import eu.davidea.flexibleadapter.items.IFlexible
+import eu.davidea.viewholders.FlexibleViewHolder
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
+import kotlinx.android.extensions.LayoutContainer
+import kotlinx.android.synthetic.main.item_account.*
+
+class LuckyNumberWidgetConfigureItem(var student: Student, val isCurrent: Boolean) :
+ AbstractFlexibleItem() {
+
+ override fun getLayoutRes() = R.layout.item_account
+
+ override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder {
+ return ViewHolder(view, adapter)
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) {
+ holder.apply {
+ accountItemName.text = "${student.studentName} ${student.className}"
+ accountItemSchool.text = student.schoolName
+ accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0)
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as TimetableWidgetConfigureItem
+
+ if (student != other.student) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = student.hashCode()
+ result = 31 * result + student.id.toInt()
+ return result
+ }
+
+ class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
+ override val containerView: View
+ get() = contentView
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
new file mode 100644
index 00000000..3f808f09
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
@@ -0,0 +1,62 @@
+package io.github.wulkanowy.ui.modules.luckynumberwidget
+
+import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
+import io.github.wulkanowy.data.db.SharedPrefHelper
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.createWidgetKey
+import io.github.wulkanowy.utils.SchedulersProvider
+import javax.inject.Inject
+
+class LuckyNumberWidgetConfigurePresenter @Inject constructor(
+ private val errorHandler: ErrorHandler,
+ private val schedulers: SchedulersProvider,
+ private val studentRepository: StudentRepository,
+ private val sharedPref: SharedPrefHelper
+) : BasePresenter(errorHandler) {
+
+ private var appWidgetId: Int? = null
+
+ fun onAttachView(view: LuckyNumberWidgetConfigureView, appWidgetId: Int?) {
+ super.onAttachView(view)
+ this.appWidgetId = appWidgetId
+ view.initView()
+ loadData()
+ }
+
+ fun onItemSelect(item: AbstractFlexibleItem<*>) {
+ if (item is LuckyNumberWidgetConfigureItem) {
+ registerStudent(item.student)
+ }
+ }
+
+ private fun loadData() {
+ disposable.add(studentRepository.getSavedStudents(false)
+ .map { it to appWidgetId?.let { id -> sharedPref.getLong(createWidgetKey(id), 0) } }
+ .map { (students, currentStudentId) ->
+ students.map { student -> LuckyNumberWidgetConfigureItem(student, student.id == currentStudentId) }
+ }
+ .subscribeOn(schedulers.backgroundThread)
+ .observeOn(schedulers.mainThread)
+ .subscribe({
+ when {
+ it.isEmpty() -> view?.openLoginView()
+ it.size == 1 -> registerStudent(it.single().student)
+ else -> view?.updateData(it)
+ }
+ }, { errorHandler.dispatch(it) }))
+ }
+
+ private fun registerStudent(student: Student) {
+ appWidgetId?.also {
+ sharedPref.putLong(createWidgetKey(it), student.id)
+ view?.apply {
+ updateLuckyNumberWidget(it)
+ setSuccessResult(it)
+ }
+ }
+ view?.finishView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt
new file mode 100644
index 00000000..49c3f1dc
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt
@@ -0,0 +1,19 @@
+package io.github.wulkanowy.ui.modules.luckynumberwidget
+
+import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureItem
+
+interface LuckyNumberWidgetConfigureView : BaseView {
+
+ fun initView()
+
+ fun updateData(data: List)
+
+ fun updateLuckyNumberWidget(widgetId: Int)
+
+ fun setSuccessResult(widgetId: Int)
+
+ fun finishView()
+
+ fun openLoginView()
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
new file mode 100644
index 00000000..40352279
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
@@ -0,0 +1,180 @@
+package io.github.wulkanowy.ui.modules.luckynumberwidget
+
+import android.annotation.TargetApi
+import android.app.PendingIntent
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
+import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED
+import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.widget.RemoteViews
+import dagger.android.AndroidInjection
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.SharedPrefHelper
+import io.github.wulkanowy.data.db.entities.LuckyNumber
+import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
+import io.github.wulkanowy.utils.SchedulersProvider
+import io.reactivex.Maybe
+import timber.log.Timber
+import javax.inject.Inject
+
+class LuckyNumberWidgetProvider : BroadcastReceiver() {
+
+ @Inject
+ lateinit var studentRepository: StudentRepository
+
+ @Inject
+ lateinit var semesterRepository: SemesterRepository
+
+ @Inject
+ lateinit var luckyNumberRepository: LuckyNumberRepository
+
+ @Inject
+ lateinit var schedulers: SchedulersProvider
+
+ @Inject
+ lateinit var appWidgetManager: AppWidgetManager
+
+ @Inject
+ lateinit var sharedPref: SharedPrefHelper
+
+ companion object {
+ fun createWidgetKey(appWidgetId: Int) = "lucky_number_widget_$appWidgetId"
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ override fun onReceive(context: Context, intent: Intent) {
+ AndroidInjection.inject(this, context)
+ when (intent.action) {
+ ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent)
+ ACTION_APPWIDGET_DELETED -> onDelete(intent)
+ ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent)
+ }
+ }
+
+ private fun onUpdate(context: Context, intent: Intent) {
+ intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId ->
+ RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
+ setTextViewText(R.id.luckyNumberWidgetNumber,
+ getLuckyNumber(sharedPref.getLong(createWidgetKey(appWidgetId), 0), appWidgetId)?.luckyNumber?.toString() ?: "#"
+ )
+ setOnClickPendingIntent(R.id.luckyNumberWidgetContainer,
+ PendingIntent.getActivity(context, 2, MainActivity.getStartIntent(context).apply {
+ putExtra(EXTRA_START_MENU_INDEX, 4)
+ }, PendingIntent.FLAG_UPDATE_CURRENT))
+ }.also {
+ setStyles(it, intent)
+ appWidgetManager.updateAppWidget(appWidgetId, it)
+ }
+ }
+ }
+
+ private fun onDelete(intent: Intent) {
+ intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let {
+ if (it != 0) sharedPref.delete(createWidgetKey(it))
+ }
+ }
+
+ private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? {
+ return try {
+ studentRepository.isStudentSaved()
+ .filter { true }
+ .flatMap { studentRepository.getSavedStudents().toMaybe() }
+ .flatMap { students ->
+ students.singleOrNull { student -> student.id == studentId }
+ .let { student ->
+ if (student != null) {
+ Maybe.just(student)
+ } else {
+ studentRepository.getCurrentStudent(false)
+ .toMaybe()
+ .doOnSuccess { sharedPref.putLong(createWidgetKey(appWidgetId), it.id) }
+ }
+ }
+ }
+ .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() }
+ .flatMap { luckyNumberRepository.getLuckyNumber(it) }
+ .subscribeOn(schedulers.backgroundThread)
+ .blockingGet()
+ } catch (e: Exception) {
+ Timber.e(e, "An error has occurred in lucky number provider")
+ null
+ }
+ }
+
+ private fun onOptionsChange(context: Context, intent: Intent) {
+ intent.extras?.let { extras ->
+ RemoteViews(context.packageName, R.layout.widget_luckynumber).apply {
+ setStyles(this, intent)
+ appWidgetManager.updateAppWidget(extras.getInt(EXTRA_APPWIDGET_ID), this)
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ private fun setStyles(views: RemoteViews, intent: Intent) {
+ val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS)
+
+ val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150
+ val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40
+
+ Timber.d("New luckynumber widget measurement: %dx%d", maxWidth, maxHeight)
+
+ when {
+ // 1x1
+ maxWidth < 150 && maxHeight < 110 -> {
+ Timber.d("Luckynumber widget size: 1x1")
+ views.run {
+ setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
+ }
+ }
+ // 1x2
+ maxWidth < 150 && maxHeight > 110 -> {
+ Timber.d("Luckynumber widget size: 1x2")
+ views.run {
+ setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE)
+ setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
+ }
+ }
+ // 2x1
+ maxWidth >= 150 && maxHeight <= 110 -> {
+ Timber.d("Luckynumber widget size: 2x1")
+ views.run {
+ setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE)
+ setViewVisibility(R.id.luckyNumberWidgetTitle, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
+ }
+ }
+ // 2x2 and bigger
+ else -> {
+ Timber.d("Luckynumber widget size: 2x2 and bigger")
+ views.run {
+ setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE)
+ setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE)
+ setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE)
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
index 37d0571a..468567f1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
@@ -15,7 +15,7 @@ import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER
import io.github.wulkanowy.utils.setOnItemClickListener
-import kotlinx.android.synthetic.main.activity_timetable_widget_configure.*
+import kotlinx.android.synthetic.main.activity_widget_configure.*
import javax.inject.Inject
class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigureView {
@@ -29,7 +29,7 @@ class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigur
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_CANCELED)
- setContentView(R.layout.activity_timetable_widget_configure)
+ setContentView(R.layout.activity_widget_configure)
intent.extras.let {
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER))
@@ -37,7 +37,7 @@ class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigur
}
override fun initView() {
- timetableWidgetConfigureRecycler.apply {
+ widgetConfigureRecycler.apply {
adapter = configureAdapter
layoutManager = SmoothScrollLinearLayoutManager(context)
}
diff --git a/app/src/main/res/drawable/background_rounded_corner.xml b/app/src/main/res/drawable/background_rounded_corner.xml
new file mode 100644
index 00000000..116973b5
--- /dev/null
+++ b/app/src/main/res/drawable/background_rounded_corner.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_widget_clover_24dp.png b/app/src/main/res/drawable/ic_widget_clover_24dp.png
new file mode 100644
index 00000000..b1a7832a
Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_clover_24dp.png differ
diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png
new file mode 100644
index 00000000..539b0a59
Binary files /dev/null and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ
diff --git a/app/src/main/res/layout/activity_timetable_widget_configure.xml b/app/src/main/res/layout/activity_widget_configure.xml
similarity index 85%
rename from app/src/main/res/layout/activity_timetable_widget_configure.xml
rename to app/src/main/res/layout/activity_widget_configure.xml
index 2a95e01d..192776e6 100644
--- a/app/src/main/res/layout/activity_timetable_widget_configure.xml
+++ b/app/src/main/res/layout/activity_widget_configure.xml
@@ -6,7 +6,7 @@
android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/api_endpoints.xml b/app/src/main/res/values/api_endpoints.xml
index ff8cd23c..e07f2b4b 100644
--- a/app/src/main/res/values/api_endpoints.xml
+++ b/app/src/main/res/values/api_endpoints.xml
@@ -14,6 +14,6 @@
- https://edu.gdansk.pl
- https://umt.tarnow.pl
- https://resman.pl
- - https://fakelog.cf
+ - http://fakelog.cf
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 685bda9f..58699c9e 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -41,7 +41,7 @@
- @android:color/white
-