Timetable widget refactor (#171)

This commit is contained in:
Rafał Borcz 2018-11-02 17:38:20 +01:00 committed by Mikołaj Pich
parent 70879945f2
commit 7f6f632b73
17 changed files with 363 additions and 86 deletions

View File

@ -160,7 +160,6 @@
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="METHOD_PARAMETERS_WRAP" value="5" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />

View File

@ -47,6 +47,20 @@
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE" />
</intent-filter>
</service>
<service
android:name=".services.widgets.TimetableWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
android:label="@string/timetable_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/provider_widget_timetable" />
</receiver>
<meta-data
android:name="io.fabric.ApiKey"

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.data.db
import android.annotation.SuppressLint
import android.content.SharedPreferences
import javax.inject.Inject
import javax.inject.Singleton
@ -7,19 +8,18 @@ import javax.inject.Singleton
@Singleton
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
fun putLong(key: String, value: Long) {
sharedPref.edit().putLong(key, value).apply()
@SuppressLint("ApplySharedPref")
fun putLong(key: String, value: Long, sync: Boolean = false) {
sharedPref.edit().putLong(key, value).apply {
if (sync) commit() else apply()
}
}
fun getLong(key: String, defaultValue: Long): Long {
return sharedPref.getLong(key, defaultValue)
}
fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return sharedPref.getBoolean(key, defaultValue)
}
fun getString(key: String, defaultValue: String): String {
return sharedPref.getString(key, defaultValue) ?: defaultValue
fun delete(key: String) {
sharedPref.edit().remove(key).apply()
}
}

View File

@ -4,11 +4,13 @@ import dagger.Module
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.services.job.SyncWorker
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider
@Module
internal abstract class BuilderModule {
@ -25,6 +27,12 @@ internal abstract class BuilderModule {
@ContributesAndroidInjector(modules = [MainModule::class])
abstract fun bindMainActivity(): MainActivity
@ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService
@ContributesAndroidInjector
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
@ContributesAndroidInjector
abstract fun bindSyncJob(): SyncWorker
}

View File

@ -12,7 +12,7 @@ import androidx.core.content.ContextCompat
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_CARD_ID_KEY
import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX
import timber.log.Timber
class GradeNotification(context: Context) : BaseNotification(context) {
@ -41,7 +41,7 @@ class GradeNotification(context: Context) : BaseNotification(context) {
.setColor(ContextCompat.getColor(context, R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, 0,
MainActivity.getStartIntent(context).putExtra(EXTRA_CARD_ID_KEY, 0),
MainActivity.getStartIntent(context).putExtra(EXTRA_START_MENU_INDEX, 0),
FLAG_UPDATE_CURRENT
)
)

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.services.widgets
import android.content.Intent
import android.widget.RemoteViewsService
import dagger.android.AndroidInjection
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
import javax.inject.Inject
class TimetableWidgetService : RemoteViewsService() {
@Inject
lateinit var timetableRepository: TimetableRepository
@Inject
lateinit var sessionRepository: SessionRepository
@Inject
lateinit var sharedPref: SharedPrefHelper
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
AndroidInjection.inject(this)
return TimetableWidgetFactory(timetableRepository, sessionRepository, sharedPref, applicationContext, intent)
}
}

View File

@ -77,9 +77,7 @@ class GradePresenter @Inject constructor(
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({ _ ->
view?.let { loadChild(it.currentPageIndex) }
}) { errorHandler.proceed(it) })
.subscribe({ view?.run { loadChild(currentPageIndex) } }) { errorHandler.proceed(it) })
}
private fun loadChild(index: Int, forceRefresh: Boolean = false) {

View File

@ -32,7 +32,8 @@ class MainActivity : BaseActivity(), MainView {
lateinit var navController: FragNavController
companion object {
const val EXTRA_CARD_ID_KEY = "cardId"
const val EXTRA_START_MENU_INDEX = "extraStartMenuIndex"
fun getStartIntent(context: Context) = Intent(context, MainActivity::class.java)
}
@ -53,7 +54,7 @@ class MainActivity : BaseActivity(), MainView {
setSupportActionBar(mainToolbar)
messageContainer = mainFragmentContainer
presenter.onAttachView(this, intent.getIntExtra(EXTRA_CARD_ID_KEY, -1))
presenter.onAttachView(this, intent.getIntExtra(EXTRA_START_MENU_INDEX, -1))
navController.initialize(startMenuIndex, savedInstanceState)
}

View File

@ -12,12 +12,12 @@ class MainPresenter @Inject constructor(
private val serviceHelper: ServiceHelper
) : BasePresenter<MainView>(errorHandler) {
fun onAttachView(view: MainView, init: Int) {
fun onAttachView(view: MainView, initMenuIndex: Int) {
super.onAttachView(view)
view.run {
cancelNotifications()
startMenuIndex = if (init != -1) init else prefRepository.startMenuIndex
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
initView()
}

View File

@ -0,0 +1,93 @@
package io.github.wulkanowy.ui.widgets.timetable
import android.content.Context
import android.content.Intent
import android.graphics.Paint.ANTI_ALIAS_FLAG
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.AdapterView.INVALID_POSITION
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.SessionRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.disposables.CompositeDisposable
import org.threeten.bp.LocalDate
import timber.log.Timber
class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository,
private val sessionRepository: SessionRepository,
private val sharedPref: SharedPrefHelper,
private val context: Context,
private val intent: Intent?
) : RemoteViewsService.RemoteViewsFactory {
private val disposable: CompositeDisposable = CompositeDisposable()
private var lessons = emptyList<Timetable>()
override fun getLoadingView() = null
override fun hasStableIds() = true
override fun getCount() = lessons.size
override fun getViewTypeCount() = 1
override fun getItemId(position: Int) = position.toLong()
override fun onCreate() {}
override fun onDataSetChanged() {
intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) }
?.let { date ->
if (sessionRepository.isSessionSaved) {
disposable.add(sessionRepository.getSemesters()
.map { it.single { item -> item.current } }
.flatMap { timetableRepository.getTimetable(it, date, date) }
.map { item -> item.sortedBy { it.number } }
.subscribe({ lessons = it })
{ Timber.e(it, "An error has occurred while downloading data for the widget") })
}
}
}
override fun getViewAt(position: Int): RemoteViews? {
if (position == INVALID_POSITION) return null
return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
lessons[position].let {
setTextViewText(R.id.timetableWidgetItemSubject, it.subject)
setTextViewText(R.id.timetableWidgetItemNumber, it.number.toString())
setTextViewText(R.id.timetableWidgetItemTime, it.start.toFormattedString("HH:mm") +
" - ${it.end.toFormattedString("HH:mm")}")
if (it.room.isNotBlank()) {
setTextViewText(R.id.timetableWidgetItemRoom, "${context.getString(R.string.timetable_room)} ${it.room}")
} else setTextViewText(R.id.timetableWidgetItemRoom, "")
if (it.info.isNotBlank()) {
setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE)
setTextViewText(R.id.timetableWidgetItemDescription, it.info.capitalize())
} else setViewVisibility(R.id.timetableWidgetItemDescription, GONE)
if (it.changes) {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags",
STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG)
} else {
setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG)
}
}
setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent())
}
}
override fun onDestroy() {
disposable.clear()
}
}

View File

@ -0,0 +1,119 @@
package io.github.wulkanowy.ui.widgets.timetable
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import dagger.android.AndroidInjection
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.services.widgets.TimetableWidgetService
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.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.weekDayName
import org.threeten.bp.LocalDate
import javax.inject.Inject
class TimetableWidgetProvider : AppWidgetProvider() {
@Inject
lateinit var sharedPref: SharedPrefHelper
companion object {
const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget"
const val EXTRA_BUTTON_TYPE = "extraButtonType"
const val BUTTON_NEXT = "buttonNext"
const val BUTTON_PREV = "buttonPrev"
const val BUTTON_RESET = "buttonReset"
}
override fun onUpdate(context: Context?, appWidgetManager: AppWidgetManager?, appWidgetIds: IntArray?) {
appWidgetIds?.forEach {
val widgetKey = "timetable_widget_$it"
checkSavedWidgetDate(widgetKey)
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0))
context?.run {
RemoteViews(packageName, R.layout.widget_timetable).apply {
setTextViewText(R.id.timetableWidgetDay, savedDate.weekDayName.capitalize())
setTextViewText(R.id.timetableWidgetDate, savedDate.toFormattedString())
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java)
.apply { action = widgetKey })
setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, it, it, appWidgetIds, BUTTON_NEXT))
setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -it, it, appWidgetIds, BUTTON_PREV))
createNavIntent(context, Int.MAX_VALUE, it, appWidgetIds, BUTTON_RESET).let { intent ->
setOnClickPendingIntent(R.id.timetableWidgetDate, intent)
setOnClickPendingIntent(R.id.timetableWidgetDay, intent)
}
setPendingIntentTemplate(R.id.timetableWidgetList,
PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply {
putExtra(EXTRA_START_MENU_INDEX, 3)
}, FLAG_UPDATE_CURRENT))
}.also { view ->
appWidgetManager?.apply {
notifyAppWidgetViewDataChanged(it, R.id.timetableWidgetList)
updateAppWidget(it, view)
}
}
}
}
}
override fun onReceive(context: Context?, intent: Intent?) {
AndroidInjection.inject(this, context)
intent?.let {
val widgetKey = "timetable_widget_${it.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)}"
when (it.getStringExtra(EXTRA_BUTTON_TYPE)) {
BUTTON_NEXT -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).nextSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
}
BUTTON_PREV -> {
LocalDate.ofEpochDay(sharedPref.getLong(widgetKey, 0)).previousSchoolDay
.let { date -> sharedPref.putLong(widgetKey, date.toEpochDay(), true) }
}
BUTTON_RESET -> sharedPref.putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true)
}
}
super.onReceive(context, intent)
}
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
appWidgetIds?.forEach {
sharedPref.delete("timetable_widget_$it")
}
}
private fun createNavIntent(context: Context, code: Int, widgetId: Int, widgetIds: IntArray, buttonType: String): PendingIntent {
return PendingIntent.getBroadcast(context, code,
Intent(context, TimetableWidgetProvider::class.java).apply {
action = ACTION_APPWIDGET_UPDATE
putExtra(EXTRA_APPWIDGET_IDS, widgetIds)
putExtra(EXTRA_BUTTON_TYPE, buttonType)
putExtra(EXTRA_TOGGLED_WIDGET_ID, widgetId)
}, FLAG_UPDATE_CURRENT)
}
private fun checkSavedWidgetDate(widgetKey: String) {
sharedPref.run {
if (getLong(widgetKey, -1) == -1L) {
putLong(widgetKey, LocalDate.now().nextOrSameSchoolDay.toEpochDay(), true)
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,67 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/timetable_widget_item_container"
android:id="@+id/timetableWidgetItemContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ic_all_divider"
android:minHeight="45dp"
android:orientation="horizontal">
<TextView
android:id="@+id/timetable_widget_item_time"
android:id="@+id/timetableWidgetItemNumber"
android:layout_width="55dp"
android:layout_height="45dp"
android:gravity="center"
android:text="0"
android:textSize="25sp" />
<TextView
android:id="@+id/timetableWidgetItemTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/timetableWidgetItemNumber"
android:layout_toRightOf="@id/timetableWidgetItemNumber"
android:text="@string/app_name"
android:textColor="@color/second_text_color"
android:textSize="14sp" />
android:textSize="13sp" />
<TextView
android:id="@+id/timetable_widget_item_subject"
android:id="@+id/timetableWidgetItemSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="25dp"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@id/timetable_widget_item_time"
android:layout_toRightOf="@id/timetable_widget_item_time"
android:layout_marginEnd="25dp"
android:layout_marginRight="25dp"
android:layout_toEndOf="@id/timetableWidgetItemTime"
android:layout_toRightOf="@id/timetableWidgetItemTime"
android:text="@string/app_name"
android:textColor="@color/second_text_color"
android:textSize="14sp" />
android:textSize="13sp" />
<TextView
android:id="@+id/timetable_widget_item_description"
android:id="@+id/timetableWidgetItemDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/timetable_widget_item_subject"
android:layout_marginBottom="5dp"
android:layout_marginEnd="25dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="25dp"
android:layout_below="@id/timetableWidgetItemSubject"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="3dp"
android:layout_toEndOf="@id/timetable_widget_item_time"
android:layout_toRightOf="@id/timetable_widget_item_time"
android:layout_marginEnd="25dp"
android:layout_marginRight="25dp"
android:layout_marginBottom="5dp"
android:layout_toEndOf="@id/timetableWidgetItemTime"
android:layout_toRightOf="@id/timetableWidgetItemTime"
android:text="@string/app_name"
android:textColor="@color/colorPrimaryDark"
android:textSize="12sp" />
<TextView
android:id="@+id/timetable_widget_item_room"
android:id="@+id/timetableWidgetItemRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/timetable_widget_item_time"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_below="@id/timetableWidgetItemTime"
android:layout_marginTop="3dp"
android:layout_marginBottom="5dp"
android:layout_toEndOf="@id/timetableWidgetItemNumber"
android:layout_toRightOf="@id/timetableWidgetItemNumber"
android:text="@string/app_name"
android:textColor="@color/second_text_color"
android:textSize="12sp" />
</RelativeLayout>

View File

@ -2,70 +2,81 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="@android:color/white">
android:background="@android:color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/timetable_widget_bar_height"
android:background="@color/colorPrimary">
<Button
android:id="@+id/timetable_widget_toggle"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
<ImageButton
android:id="@+id/timetableWidgetPrev"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:backgroundTint="@color/colorPrimaryDark"
android:text="@string/widget_timetable_today"
android:textColor="@android:color/white"
android:textSize="12sp" />
android:contentDescription="@string/all_prev"
android:src="@drawable/ic_widget_chevron_24dp" />
<TextView
android:id="@+id/timetable_widget_date"
android:id="@+id/timetableWidgetDay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="5dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_toEndOf="@id/timetable_widget_title"
android:layout_toRightOf="@id/timetable_widget_title"
android:ellipsize="end"
android:gravity="center"
android:layout_alignTop="@+id/timetableWidgetPrev"
android:layout_centerHorizontal="true"
android:layout_toStartOf="@id/timetableWidgetNext"
android:layout_toLeftOf="@id/timetableWidgetNext"
android:layout_toEndOf="@id/timetableWidgetPrev"
android:layout_toRightOf="@id/timetableWidgetPrev"
android:gravity="center_horizontal"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="13sp" />
android:textSize="15sp" />
<TextView
android:id="@+id/timetable_widget_title"
android:layout_width="match_parent"
android:layout_height="@dimen/timetable_widget_bar_height"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_toLeftOf="@id/timetable_widget_toggle"
android:layout_toStartOf="@id/timetable_widget_toggle"
android:ellipsize="end"
android:gravity="center_vertical"
android:id="@+id/timetableWidgetDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/timetableWidgetDay"
android:layout_centerInParent="true"
android:layout_marginTop="3dp"
android:layout_toStartOf="@id/timetableWidgetNext"
android:layout_toLeftOf="@id/timetableWidgetNext"
android:layout_toEndOf="@id/timetableWidgetPrev"
android:layout_toRightOf="@id/timetableWidgetPrev"
android:gravity="center_horizontal"
android:maxLines="1"
android:text="@string/timetable_title"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="20sp" />
android:textSize="14sp" />
<ImageButton
android:id="@+id/timetableWidgetNext"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:backgroundTint="@color/colorPrimaryDark"
android:contentDescription="@string/all_next"
android:rotation="180"
android:src="@drawable/ic_widget_chevron_24dp" />
</RelativeLayout>
<ListView
android:id="@+id/timetable_widget_list"
android:id="@+id/timetableWidgetList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/timetable_widget_bar_height" />
<TextView
android:id="@+id/timetable_widget_empty"
android:id="@+id/timetableWidgetEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_timetable"
android:minHeight="100dp"
android:minWidth="150dp"
android:minHeight="100dp"
android:previewImage="@drawable/img_timetable_widget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="3600000"
android:updatePeriodMillis="7200000"
android:widgetCategory="home_screen" />