Commit 888c707d authored by Filip Wiesner's avatar Filip Wiesner

Widget done

parent 0c32610d
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cvut.blackbird">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".BlackBirdAC"
android:allowBackup="true"
android:icon="@drawable/ic_raven"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".flows.BlackBirdMain">
<nav-graph android:value="@navigation/nav_graph" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="callback"
android:scheme="kosapp" />
</intent-filter>
</activity>
<receiver android:name="com.cvut.blackbird.flows.widget.NextEventWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/next_event_widget_info" />
</receiver>
</application>
package="com.cvut.blackbird">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".BlackBirdAC"
android:allowBackup="true"
android:icon="@drawable/ic_raven"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".flows.BlackBirdMain">
<nav-graph android:value="@navigation/nav_graph" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="callback"
android:scheme="kosapp" />
</intent-filter>
</activity>
<service
android:name="com.cvut.blackbird.flows.widget.NextEventService"
android:exported="true"
android:enabled="true"/>
<receiver android:name="com.cvut.blackbird.flows.widget.NextEventWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/next_event_widget_info" />
</receiver>
</application>
</manifest>
package com.cvut.blackbird
import android.app.Application
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.util.Log
import androidx.fragment.app.FragmentManager
import com.chibatching.kotpref.Kotpref
import com.cvut.blackbird.dinjection.components.ApplicationComponent
import com.cvut.blackbird.dinjection.components.DaggerApplicationComponent
import com.cvut.blackbird.dinjection.modules.ServicesModule
import com.cvut.blackbird.dinjection.modules.RoomModule
import com.cvut.blackbird.dinjection.modules.ServicesModule
import net.danlew.android.joda.JodaTimeAndroid
import androidx.core.view.accessibility.AccessibilityEventCompat.setAction
import android.content.Intent
import com.cvut.blackbird.flows.widget.NextEventWidget
class BlackBirdAC: Application(){
class BlackBirdAC: Application() {
companion object {
const val LOG_TAG = "BLACK_BIRD"
//platformStatic allow access it from java code
......@@ -35,11 +30,5 @@ class BlackBirdAC: Application(){
JodaTimeAndroid.init(this)
Kotpref.init(this)
val intent = Intent(this, NextEventWidget::class.java)
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(this).getAppWidgetIds(ComponentName(this, NextEventWidget::class.java))
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)
}
}
package com.cvut.blackbird.extensions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.Main
import kotlinx.coroutines.withContext
suspend fun <T> CoroutineScope.onMainThread(
job: suspend CoroutineScope.() -> T
) = withContext(Dispatchers.Main, job)
\ No newline at end of file
......@@ -6,3 +6,21 @@ val density = Resources.getSystem().displayMetrics.density
val Int.dpToPx: Int get() = (this * density).toInt()
val Float.dpToPx: Float get() = this * density
fun Long.millisToTimeText() = buildString {
val time = this@millisToTimeText / 1000 / 60
append("in ")
if (time < 60) // Less than a hour
append(time.toString() + " min")
else if (time < 1440) { // Less than a day
if (time / 60 > 0) append((time / 60).toString() + " h ")
if (time % 60 > 0) append((time % 60).toString() + " min")
else delete(length - 1, length)
} else {
append((time / 1440).toString() + " d ")
if ((time % 1440) / 60 > 0) append(((time % 1440) / 60).toString() + " h")
else delete(length - 1, length)
}
}
package com.cvut.blackbird.extensions
//import io.reactivex.Observable
//import io.reactivex.disposables.Disposable
//import io.reactivex.functions.Consumer
//import io.reactivex.schedulers.Schedulers
//import io.reactivex.subjects.BehaviorSubject
//
//
//class Variable<T>(default: T) {
// val subject: BehaviorSubject<T> = BehaviorSubject.createDefault(default)
//
// var value: T
// get() = subject.value!!
// set(value) = subject.onNext(value)
//
// fun subscribe(onNext: Consumer<in T>): Disposable = subject.subscribe(onNext)
//
// fun asObservable(): Observable<T> = subject.subscribeOn(Schedulers.newThread())
//}
//
//infix fun Disposable.disposeTo(bag: MutableList<Disposable>) {
// bag.add(this)
//}
\ No newline at end of file
package com.cvut.blackbird.flows.widget
import android.app.Service
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Intent
import android.os.IBinder
import androidx.room.Room
import com.cvut.blackbird.model.database.BlackBirdDB
import org.jetbrains.anko.toast
class NextEventService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
sendBroadcast(Intent(this, NextEventWidget::class.java).apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
putExtra(
AppWidgetManager.EXTRA_APPWIDGET_IDS,
AppWidgetManager.getInstance(this@NextEventService)
.getAppWidgetIds(ComponentName(this@NextEventService, NextEventWidget::class.java))
)
})
// val db: BlackBirdDB = Room
// .databaseBuilder(this, BlackBirdDB::class.java, "blackbird_DB")
// .fallbackToDestructiveMigration()
// .build()
//
// val appWidgetManager = AppWidgetManager.getInstance(this)
// val allWidgetIds = intent?.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)
//
// if (allWidgetIds != null) {
// for (appWidgetId in allWidgetIds) {
// NextEventWidget.updateAppWidget(this, appWidgetManager, appWidgetId, db)
// }
// }
// toast("next service RUNNING")
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? = null
}
package com.cvut.blackbird.flows.widget
import android.app.AlarmManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import androidx.room.Room
import com.cvut.blackbird.R
import com.cvut.blackbird.extensions.courseAbbr
import com.cvut.blackbird.extensions.millisToTimeText
import com.cvut.blackbird.model.database.BlackBirdDB
import com.cvut.blackbird.model.entities.EventType
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.jetbrains.anko.toast
import org.joda.time.DateTime
import kotlin.reflect.jvm.jvmName
/**
* Implementation of App Widget functionality.
*/
class NextEventWidget : AppWidgetProvider() {
private var pendingIntent: PendingIntent? = null
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
val db: BlackBirdDB = Room
.databaseBuilder(context, BlackBirdDB::class.java, "blackbird_DB")
.fallbackToDestructiveMigration()
.build()
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds)
updateAppWidget(context, appWidgetManager, appWidgetId, db)
}
override fun onEnabled(context: Context) {
// Enter relevant functionality for when the first widget is created
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
/** STATIC */
companion object {
private const val updateInterval: Long = 1000 * 60
internal fun updateAppWidget(
context: Context,
......@@ -51,44 +37,78 @@ class NextEventWidget : AppWidgetProvider() {
database: BlackBirdDB
) {
// Construct the RemoteViews object
context.toast("")
val views = RemoteViews(context.packageName, R.layout.next_event_widget)
runBlocking { GlobalScope.launch {
val nextEvent = database.eventDao().nextEvent()
nextEvent?.let {
val time = (it.event.startsAt.millis - DateTime.now().millis) / 1000
views.setTextViewText(R.id.widget_timeLeft, time.millisToTimeText())
views.setTextViewText(R.id.widget_courseIcon, it.courseEnt?.code?.courseAbbr)
views.setInt(R.id.widget_courseIcon, "setBackground",
when(it.event.eventType) {
EventType.LECTURE -> R.drawable.ic_timetable_lecture
EventType.TUTORIAL -> R.drawable.ic_timetable_tutorial
else -> R.drawable.ic_timetable_info
})
}
} }
val event = runBlocking { GlobalScope.async { database.eventDao().nextEvent() }.await() }
context.toast("Widget ${event?.courseEnt?.code} updated")
event?.let {
val time = (it.event.startsAt.millis - DateTime.now().millis)
views.setTextViewText(R.id.widget_timeLeft, time.millisToTimeText())
views.setTextViewText(R.id.widget_courseIcon, it.courseEnt?.code?.courseAbbr)
views.setInt(R.id.widget_courseIcon, "setBackgroundResource",
when(it.event.eventType) {
EventType.LECTURE -> R.drawable.ic_timetable_lecture
EventType.TUTORIAL -> R.drawable.ic_timetable_tutorial
else -> R.drawable.ic_timetable_info
})
}
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
fun Long.millisToTimeText() = buildString {
val time = this@millisToTimeText
append("in ")
if (time < 60) append(time.toString() + " min")
else {
if (time / 60 > 0) append((time / 60).toString() + " h ")
if (time % 60 > 0) append((time % 60).toString() + " min")
else {
delete(length - 2, length)
append(" h")
/** HELPER FUNCTIONS */
private fun createPending(context: Context) = Intent(
context, NextEventService::class.java
).let {
PendingIntent.getService(context, 0, it, PendingIntent.FLAG_CANCEL_CURRENT)
}
private fun updateInMillis(millis: Long, pendingIntent: PendingIntent, context: Context) {
val alarm = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarm.set(AlarmManager.ELAPSED_REALTIME, millis, pendingIntent)
}
}
/** OVERRIDDEN METHODS */
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
val db: BlackBirdDB = Room
.databaseBuilder(context, BlackBirdDB::class.java, "blackbird_DB")
.fallbackToDestructiveMigration()
.build()
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds)
updateAppWidget(context, appWidgetManager, appWidgetId, db)
if (pendingIntent == null) pendingIntent = createPending(context)
updateInMillis(updateInterval, pendingIntent!!, context)
}
override fun onEnabled(context: Context) {
// if(pendingIntent == null)
// pendingIntent = createPending(context)
//
// val alarm = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
//
// alarm.setRepeating(
// AlarmManager.ELAPSED_REALTIME,
// SystemClock.elapsedRealtime() + updateInterval,
// updateInterval,
// pendingIntent
// )
// updateInMillis(5000, pendingIntent!!, context)
// alarm.setExact(AlarmManager.ELAPSED_REALTIME, 1000 * 5, pending)
}
override fun onDisabled(context: Context) {
val alarm = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarm.cancel(pendingIntent ?: createPending(context))
}
}
......@@ -21,14 +21,14 @@
android:lines="1"
tools:text="in 8 min"
android:textColor="@color/colorTextLight"
android:textSize="12sp"
android:textSize="11sp"
android:textStyle="bold" />
<TextView
android:id="@+id/widget_courseIcon"
android:layout_width="52dp"
android:layout_height="52dp"
android:background="@drawable/ic_timetable_lecture"
tools:background="@drawable/ic_timetable_lecture"
android:fontFamily="sans-serif-condensed"
android:gravity="center"
android:textColor="@android:color/white"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment