Compare commits

...

10 Commits

21 changed files with 330 additions and 37 deletions

View File

@ -98,7 +98,7 @@
<service android:name=".ui.widgets.notifications.WidgetNotificationsService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<!-- LUCKY NUMBER -->
<receiver android:name=".widgets.luckynumber.WidgetLuckyNumber"
<receiver android:name=".ui.widgets.luckynumber.WidgetLuckyNumberProvider"
android:label="@string/widget_lucky_number_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

View File

@ -1,4 +1,4 @@
<h3>Wersja 4.0-beta.2, 2020-01-06</h3>
<h3>Wersja 4.0-beta.3, 2020-01-10</h3>
<ul>
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli &#x1F44F;</li>
@ -23,7 +23,6 @@
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
<ul>
<li>Wysyłanie wiadomości może czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
<li>Widget szczęśliwego numerka</li>
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
</ul>
<br>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0xf5, 0xbe, 0x91, 0x89, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0x1e, 0xef, 0x4e, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -105,6 +105,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank()
}
fun currentTimeUnix() = System.currentTimeMillis() / 1000
fun Bundle?.getInt(key: String, defaultValue: Int): Int {

View File

@ -40,6 +40,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
private var mHash: String? = null
var hash: String
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value }
private var mAppVersion: Int? = null
var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
@ -90,11 +95,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
ConfigMigration(app, this)
}
fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
}
fun forProfile(): ProfileConfig {
return profileConfigs[App.profileId] ?: ProfileConfig(db, App.profileId, rawEntries)
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, db.configDao().getAllNow(profileId)).also {
profileConfigs[profileId] = it
}
}
fun forProfile() = getFor(App.profileId)
fun setProfile(profileId: Int) {
}

View File

@ -37,6 +37,11 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
private var mHash: String? = null
var hash: String
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value }
init {
rawEntries.toHashMap(profileId, values)
/*if (dataVersion < DATA_VERSION)

View File

@ -8,7 +8,7 @@ import kotlin.text.RegexOption.DOT_MATCHES_ALL
object Regexes {
val STYLE_CSS_COLOR by lazy {
"""color: \w+?;?"?""".toRegex()
"""color: (\w+);?""".toRegex()
}

View File

@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebGrades(override val data: DataIdziennik,
@ -89,11 +89,17 @@ class IdziennikWebGrades(override val data: DataIdziennik,
count += weight
}
val historyColor = historyItem.getString("Kolor") ?: ""
colorInt = 0xff2196f3.toInt()
if (historyColor.isNotEmpty()) {
colorInt = Color.parseColor("#$historyColor")
}
val historyObject = Grade(
profileId,
gradeObject.id * -1,
historyItem.get("Kategoria").asString,
Color.parseColor("#" + historyItem.get("Kolor").asString),
colorInt,
historyItem.get("Uzasadnienie").asString,
historyItem.get("Ocena").asString,
value,

View File

@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.md5
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import retrofit2.Retrofit
@ -61,25 +62,45 @@ class SzkolnyApi(val app: App) {
val response = api.serverSync(ServerSyncRequest(
deviceId = app.deviceId,
device = ServerSyncRequest.Device(
osType = "Android",
osVersion = Build.VERSION.RELEASE,
hardware = "${Build.MANUFACTURER} ${Build.MODEL}",
pushToken = app.config.sync.tokenApp,
appVersion = BuildConfig.VERSION_NAME,
appType = BuildConfig.BUILD_TYPE,
appVersionCode = BuildConfig.VERSION_CODE,
syncInterval = app.config.sync.interval
),
device = run {
val config = app.config
val device = ServerSyncRequest.Device(
osType = "Android",
osVersion = Build.VERSION.RELEASE,
hardware = "${Build.MANUFACTURER} ${Build.MODEL}",
pushToken = app.config.sync.tokenApp,
appVersion = BuildConfig.VERSION_NAME,
appType = BuildConfig.BUILD_TYPE,
appVersionCode = BuildConfig.VERSION_CODE,
syncInterval = app.config.sync.interval
)
device.toString().md5().let {
if (it == config.hash)
null
else {
config.hash = it
device
}
}
},
userCodes = profiles.map { it.userCode },
users = profiles.map { profile ->
ServerSyncRequest.User(
users = profiles.mapNotNull { profile ->
val config = app.config.getFor(profile.id)
val user = ServerSyncRequest.User(
profile.userCode,
profile.studentNameLong ?: "",
profile.studentNameShort ?: "",
profile.loginStoreType,
teams.filter { it.profileId == profile.id }.map { it.code }
)
user.toString().md5().let {
if (it == config.hash)
null
else {
config.hash = it
user
}
}
},
notifications = notifications.map { ServerSyncRequest.Notification(it.profileName ?: "", it.type, it.text) }
)).execute().body()

View File

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MD9c4qHZ3B===.$param2".sha256()
return "$param1.MTIzNDU2Nzg5MDJ9J602xT===.$param2".sha256()
}
}

View File

@ -36,8 +36,8 @@ public class EventFull extends Event {
event.color,
event.type,
event.addedManually,
event.subjectId,
event.teacherId,
event.subjectId,
event.teamId
);

View File

@ -4,6 +4,10 @@
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.content.Intent
import android.provider.CalendarContract
import android.provider.CalendarContract.Events
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
@ -101,6 +105,21 @@ class EventDetailsDialog(
Date.fromMillis(event.addedDate).formattedString,
event.sharedByName ?: event.teacherFullName ?: ""
)
b.editButton.visibility = if (event.addedManually) View.VISIBLE else View.GONE
b.editButton.setOnClickListener {
EventManualDialog(
activity,
event.profileId,
editingEvent = event,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
b.saveInCalendarButton.setOnClickListener {
openInCalendar()
}
}
private fun showRemoveEventDialog() {
@ -133,7 +152,7 @@ class EventDetailsDialog(
Toast.makeText(activity, "Unshare + remove own event", Toast.LENGTH_SHORT).show()
val response = withContext(Dispatchers.Default) {
api.unshareEvent(event!!)
api.unshareEvent(event)
}
response?.errors?.ifNotEmpty {
@ -165,4 +184,31 @@ class EventDetailsDialog(
if (activity is MainActivity && activity.navTargetId == MainActivity.DRAWER_ITEM_AGENDA)
activity.reloadTarget()
}
private fun openInCalendar() { launch {
val title = (event.typeName ?: "") +
(if (event.typeName.isNotNullNorBlank() && event.subjectLongName.isNotNullNorBlank()) " - " else " ") +
(event.subjectLongName ?: "")
val intent = Intent(Intent.ACTION_EDIT).apply {
data = Events.CONTENT_URI
putExtra(Events.TITLE, title)
putExtra(Events.DESCRIPTION, event.topic)
if (event.startTime == null) {
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true)
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, event.eventDate.inMillis)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, event.eventDate.inMillis)
} else {
val startTime = event.eventDate.combineWith(event.startTime)
val endTime = startTime + 45 * 60 * 1000 /* 45 min */
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, false)
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startTime)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime)
}
}
activity.startActivity(intent)
}}
}

View File

@ -6,11 +6,16 @@ package pl.szczodrzynski.edziennik.ui.modules.home
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.ActivityCounterBinding
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncTimeChooseDialog
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
@ -32,7 +37,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
private val syncedNow: Time
get() = Time.fromMillis(Time.getNow().inMillis - bellSyncDiffMillis)
private val countInSeconts: Boolean
private val countInSeconds: Boolean
get() = app.config.timetable.countInSeconds
override fun onCreate(savedInstanceState: Bundle?) {
@ -54,6 +59,13 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
}
}
b.bellSync.setImageDrawable(IconicsDrawable(this@CounterActivity, SzkolnyFont.Icon.szf_alarm_bell_outline)
.colorInt(0xff404040.toInt())
.sizeDp(36))
b.bellSync.onClick {
BellSyncTimeChooseDialog(activity = this@CounterActivity)
}
app.config.timetable.bellSyncDiff?.let {
bellSyncDiffMillis = (it.hour * 60 * 60 * 1000 + it.minute * 60 * 1000 + it.second * 1000).toLong()
bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
@ -85,13 +97,13 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
b.lessonName.text = actual.displaySubjectName
val left = actual.displayEndTime!! - now
b.timeLeft.text = timeLeft(left.toInt(), "\n", countInSeconts)
b.timeLeft.text = timeLeft(left.toInt(), "\n", countInSeconds)
}
next != null -> {
b.lessonName.text = next.displaySubjectName
val till = next.displayStartTime!! - now
b.timeLeft.text = timeTill(till.toInt(), "\n", countInSeconts)
b.timeLeft.text = timeTill(till.toInt(), "\n", countInSeconds)
}
else -> {
b.lessonName.text = app.getString(R.string.lessons_finished)

View File

@ -27,9 +27,9 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.databinding.DialogWidgetConfigBinding;
import pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider;
import pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider;
import pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider;
import pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber;
import static pl.szczodrzynski.edziennik.ExtensionsKt.filterOutArchived;
@ -112,7 +112,7 @@ public class WidgetConfigActivity extends Activity {
.backgroundColor(Color.WHITE)
.build());
}
if (profileList.size() > 1) {
if (profileList.size() > 1 && widgetType != WIDGET_LUCKY_NUMBER) {
adapter.add(
new MaterialSimpleListItem.Builder(this)
.id(-1)
@ -150,7 +150,7 @@ public class WidgetConfigActivity extends Activity {
refreshIntent = new Intent(app.getContext(), WidgetNotificationsProvider.class);
break;
case WIDGET_LUCKY_NUMBER:
refreshIntent = new Intent(app.getContext(), WidgetLuckyNumber.class);
refreshIntent = new Intent(app.getContext(), WidgetLuckyNumberProvider.class);
break;
}
refreshIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-7.
*/
package pl.szczodrzynski.edziennik.ui.widgets.luckynumber
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.RemoteViews
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
class WidgetLuckyNumberProvider : AppWidgetProvider() {
companion object {
private const val TAG = "WidgetLuckyNumberProvider"
}
private fun getRemoteViews(app: App, config: WidgetConfig): RemoteViews {
return if (config.bigStyle) {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.widget_lucky_number_dark_big else R.layout.widget_lucky_number_big)
} else {
RemoteViews(app.packageName, if (config.darkTheme) R.layout.widget_lucky_number_dark else R.layout.widget_lucky_number)
}
}
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
for (appWidgetId in appWidgetIds) {
val config = widgetConfigs.getJsonObject(appWidgetId.toString())?.let { app.gson.fromJson(it, WidgetConfig::class.java) } ?: continue
val views = getRemoteViews(app, config)
val today = Date.getToday()
val todayValue = today.value
val tomorrow = Date.getToday().stepForward(0, 0, 1)
val tomorrowValue = tomorrow.value
val profile = app.db.profileDao().getByIdNow(config.profileId)
val luckyNumber = app.db.luckyNumberDao().getNearestFutureNow(config.profileId, todayValue)
val isYours = luckyNumber?.number == profile?.studentNumber
var noNumberText = false
if (profile != null) {
views.setTextViewText(R.id.widgetLuckyNumberProfileRight, profile.name)
views.setTextViewText(R.id.widgetLuckyNumberProfileBottom, profile.name)
}
if (profile == null || luckyNumber == null || luckyNumber.number == -1) {
noNumberText = true
views.setTextViewText(R.id.widgetLuckyNumberTextRight, null)
views.setTextViewText(R.id.widgetLuckyNumberTextBottom, null)
}
else {
views.setTextViewText(R.id.widgetLuckyNumberTextRight, luckyNumber.number.toString())
views.setTextViewText(R.id.widgetLuckyNumberTextBottom, luckyNumber.number.toString())
}
val drawableRes = when {
luckyNumber == null || luckyNumber.number == -1 -> R.drawable.emoji_sad
isYours -> R.drawable.emoji_glasses
!isYours -> R.drawable.emoji_smiling
else -> R.drawable.emoji_no_face
}
views.setViewVisibility(R.id.widgetLuckyNumberTextRightLayout, if (noNumberText) View.GONE else View.VISIBLE)
views.setViewVisibility(R.id.widgetLuckyNumberTextBottomLayout, if (noNumberText) View.GONE else View.VISIBLE)
views.setImageViewResource(R.id.widgetLuckyNumberIcon, drawableRes)
updateLayout(config, views, appWidgetManager, appWidgetId)
val openIntent = Intent(context, MainActivity::class.java)
openIntent.action = Intent.ACTION_MAIN
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOME)
val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0)
views.setOnClickPendingIntent(R.id.widgetLuckyNumberRoot, openPendingIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
private fun updateLayout(config: WidgetConfig, views: RemoteViews, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
val minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
val minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
val width = Utils.getCellsForSize(minWidth)
val height = Utils.getCellsForSize(minHeight)
when (width) {
1 -> {
views.setViewVisibility(R.id.widgetLuckyNumberProfileRight, View.GONE)
views.setViewVisibility(R.id.widgetLuckyNumberProfileBottom, View.GONE)
views.setViewVisibility(R.id.widgetLuckyNumberTextRight, View.GONE)
views.setViewVisibility(R.id.widgetLuckyNumberTextBottom, View.VISIBLE)
}
2 -> {
views.setViewVisibility(R.id.widgetLuckyNumberProfileRight, View.GONE)
views.setViewVisibility(R.id.widgetLuckyNumberProfileBottom, View.VISIBLE)
views.setViewVisibility(R.id.widgetLuckyNumberTextRight, View.VISIBLE)
views.setViewVisibility(R.id.widgetLuckyNumberTextBottom, View.GONE)
}
else -> {
views.setViewVisibility(R.id.widgetLuckyNumberProfileRight, if (config.bigStyle) View.GONE else View.VISIBLE)
views.setViewVisibility(R.id.widgetLuckyNumberProfileBottom, if (config.bigStyle) View.VISIBLE else View.GONE)
views.setViewVisibility(R.id.widgetLuckyNumberTextRight, View.VISIBLE)
views.setViewVisibility(R.id.widgetLuckyNumberTextBottom, View.GONE)
}
}
}
override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) {
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
val config = widgetConfigs.getJsonObject(appWidgetId.toString())?.let { app.gson.fromJson(it, WidgetConfig::class.java) } ?: return
val views: RemoteViews = getRemoteViews(app, config)
updateLayout(config, views, appWidgetManager, appWidgetId)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
val app = context.applicationContext as App
val widgetConfigs = app.config.widgetConfigs
appWidgetIds.forEach {
widgetConfigs.remove(it.toString())
}
app.config.widgetConfigs = widgetConfigs
}
}

View File

@ -219,7 +219,7 @@ class WidgetTimetableProvider : AppWidgetProvider() {
var lessons = lessonList.filter {
it.profileId == profile.id
&& it.displayDate == timetableDate
&& it.displayEndTime > now
/*&& it.displayEndTime > now*/
&& !(it.isCancelled && ignoreCancelled)
}
while ((lessons.isEmpty() || lessons.none {

View File

@ -8,6 +8,30 @@
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/counter_activity_sync"
android:textSize="12sp"
android:textColor="#ff606060" />
<ImageView
android:id="@+id/bellSync"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="10dp"
tools:tint="#ff404040"
tools:src="@sample/settings" />
</LinearLayout>
<TextView
android:id="@+id/lessonName"
android:layout_width="wrap_content"

View File

@ -149,6 +149,34 @@
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/editButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:minWidth="0dp"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:text="\uFC92"
android:textSize="20sp"
android:fontFamily="@font/community_material_font_v3_5_95_1" />
<com.google.android.material.button.MaterialButton
android:id="@+id/saveInCalendarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:minWidth="0dp"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:text="\uFB09"
android:textSize="20sp"
android:fontFamily="@font/community_material_font_v3_5_95_1" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</layout>

View File

@ -6,7 +6,10 @@
android:layout_height="match_parent"
android:background="@drawable/widget_background"
android:gravity="center"
android:orientation="vertical">
android:orientation="vertical"
tools:layout_width="200dp"
tools:layout_height="100dp"
tools:layout_gravity="center">
<LinearLayout
android:layout_width="wrap_content"
@ -18,7 +21,7 @@
android:id="@+id/widgetLuckyNumberIcon"
android:layout_width="40dp"
android:layout_height="40dp"
tools:srcCompat="@android:drawable/btn_star_big_on" />
tools:srcCompat="@drawable/emoji_no_face" />
<LinearLayout
android:layout_width="wrap_content"

View File

@ -1157,4 +1157,5 @@
<string name="event_manual_share">Udostępniam wydarzenie...</string>
<string name="event_manual_unshare_remove">Usuwam udostępnione wydarzenie...</string>
<string name="event_manual_remove">Usuwam wydarzenie...</string>
<string name="counter_activity_sync">Synchronizuj</string>
</resources>

View File

@ -5,8 +5,8 @@ buildscript {
kotlin_version = '1.3.50'
release = [
versionName: "4.0-beta.2",
versionCode: 4000002
versionName: "4.0-beta.3",
versionCode: 4000003
]
setup = [