Merge branch 'develop' into feature/code-publish

This commit is contained in:
Kuba Szczodrzyński 2021-03-28 22:22:37 +02:00
commit fe90fc6442
122 changed files with 3158 additions and 2477 deletions

View File

@ -135,10 +135,11 @@ dependencies {
implementation "eu.szkolny:agendacalendarview:1799f8ef47"
implementation "eu.szkolny:cafebar:5bf0c618de"
implementation "eu.szkolny.fslogin:lib:2.0.0"
implementation "eu.szkolny:material-about-library:0534abf316"
implementation "eu.szkolny:material-about-library:1d5ebaf47c"
implementation "eu.szkolny:mhttp:af4b62e6e9"
implementation "eu.szkolny:nachos:0e5dfcaceb"
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
implementation "eu.szkolny:ssl-provider:1.0.0"
implementation "pl.szczodrzynski:navlib:0.7.2"
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
implementation "pl.szczodrzynski:recyclertablayout:700f980584"
@ -149,7 +150,7 @@ dependencies {
implementation "com.mikepenz:iconics-core:5.2.8"
implementation "com.mikepenz:iconics-views:5.2.8"
implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar"
implementation "eu.szkolny:szkolny-font:1dab7d64ed"
implementation "eu.szkolny:szkolny-font:1.3"
// Other dependencies
implementation "cat.ereza:customactivityoncrash:2.3.0"
@ -159,6 +160,7 @@ dependencies {
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2"
implementation "com.github.bassaer:chatmessageview:2.0.1"
implementation "com.github.CanHub:Android-Image-Cropper:2.2.2"
implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
implementation "com.github.jetradarmobile:android-snowfall:1.2.0"
implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31"
@ -167,7 +169,6 @@ dependencies {
implementation "com.jaredrummler:colorpicker:1.1.0"
implementation "com.qifan.powerpermission:powerpermission-coroutines:1.3.0"
implementation "com.qifan.powerpermission:powerpermission:1.3.0"
implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0"
implementation "com.wdullaer:materialdatetimepicker:4.2.3"
implementation "com.yuyh.json:jsonviewer:1.0.6"
implementation "io.coil-kt:coil:1.1.1"

View File

@ -143,8 +143,7 @@
<activity android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
<activity android:name="com.canhub.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/Base.Theme.AppCompat" />
<activity android:name=".ui.modules.base.BuildInvalidActivity" />

View File

@ -26,6 +26,8 @@ import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics
import eu.szkolny.sslprovider.SSLProvider
import eu.szkolny.sslprovider.enableSupportedTls
import im.wangchao.mhttp.MHttp
import kotlinx.coroutines.*
import me.leolin.shortcutbadger.ShortcutBadger
@ -44,6 +46,7 @@ import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.*
import timber.log.Timber
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext
@ -51,8 +54,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
companion object {
@Volatile
lateinit var db: AppDb
val config: Config by lazy { Config(db) }
var profile: Profile by mutableLazy { Profile(0, 0, 0, "") }
lateinit var config: Config
lateinit var profile: Profile
val profileId
get() = profile.id
@ -95,7 +98,10 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
| __ | | | | | | ___/
| | | | | | | | | |
|_| |_| |_| |_| |*/
val http: OkHttpClient by lazy {
lateinit var http: OkHttpClient
lateinit var httpLazy: OkHttpClient
private fun buildHttp() {
val builder = OkHttpClient.Builder()
.cache(null)
.followRedirects(true)
@ -105,9 +111,9 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
builder.installHttpsSupport(this)
.enableSupportedTls(enableCleartext = true)
if (devMode || BuildConfig.DEBUG) {
if (devMode) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
@ -116,13 +122,14 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
builder.addInterceptor(chuckerInterceptor)
}
builder.build()
}
val httpLazy: OkHttpClient by lazy {
http.newBuilder()
http = builder.build()
httpLazy = http.newBuilder()
.followRedirects(false)
.followSslRedirects(false)
.build()
MHttp.instance().customOkHttpClient(http)
}
val cookieJar by lazy { DumbCookieJar(this) }
@ -159,32 +166,46 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.errorActivity(CrashActivity::class.java)
.apply()
Iconics.init(applicationContext)
// initialize companion object values
App.db = AppDb(this)
Themes.themeInt = config.ui.theme
devMode = config.debugMode
MHttp.instance().customOkHttpClient(http)
App.config = Config(App.db)
App.profile = Profile(0, 0, 0, "")
debugMode = BuildConfig.DEBUG
devMode = config.debugMode || debugMode
if (!profileLoadById(config.lastProfileId)) {
db.profileDao().firstId?.let { profileLoadById(it) }
}
buildHttp()
Themes.themeInt = config.ui.theme
config.ui.language?.let {
setLanguage(it)
}
debugMode = BuildConfig.DEBUG
if (BuildConfig.DEBUG)
devMode = true
Signing.getCert(this)
launch {
withContext(Dispatchers.Default) {
config.migrate(this@App)
SSLProvider.install(
applicationContext,
downloadIfNeeded = true,
supportTls13 = false,
onFinish = {
buildHttp()
},
onError = {
Timber.e("Failed to install SSLProvider: $it")
it.printStackTrace()
}
)
if (config.devModePassword != null)
checkDevModePassword()
devMode = debugMode || config.debugMode
if (config.sync.enabled)
SyncWorker.scheduleNext(this@App, false)

View File

@ -1,12 +1,9 @@
package pl.szczodrzynski.edziennik
import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Resources
import android.database.Cursor
@ -29,7 +26,6 @@ import android.view.View
import android.view.WindowManager
import android.widget.*
import androidx.annotation.*
import androidx.core.app.ActivityCompat
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
@ -40,7 +36,6 @@ import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
import com.google.android.gms.security.ProviderInstaller
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.gson.*
@ -50,10 +45,7 @@ import im.wangchao.mhttp.Response
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.TlsVersion
import okio.Buffer
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.ApiError
@ -63,7 +55,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.network.TLSSocketFactory
import pl.szczodrzynski.edziennik.utils.models.Time
import java.io.InterruptedIOException
import java.io.PrintWriter
@ -73,17 +64,13 @@ import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.nio.charset.Charset
import java.security.KeyStore
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.CRC32
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLException
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import kotlin.Pair
@ -304,19 +291,6 @@ fun colorFromCssName(name: String): Int {
fun List<Profile>.filterOutArchived() = this.filter { !it.archived }
fun Activity.isStoragePermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
true
} else {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
false
}
} else {
true
}
}
fun Response?.getUnixDate(): Long {
val rfcDate = this?.headers()?.get("date") ?: return currentTimeUnix()
val pattern = "EEE, dd MMM yyyy HH:mm:ss Z"
@ -1107,40 +1081,6 @@ fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))
fun OkHttpClient.Builder.installHttpsSupport(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(context)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
}
fun CharSequence.containsAll(list: List<CharSequence>, ignoreCase: Boolean = false): Boolean {
for (i in list) {
if (!contains(i, ignoreCase))
@ -1278,3 +1218,41 @@ operator fun <K, V> Iterable<Pair<K, V>>.get(key: K): V? {
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
fun <E> MutableList<E>.after(what: E, insert: E) {
val index = indexOf(what)
if (index != -1)
add(index + 1, insert)
}
fun <E> MutableList<E>.before(what: E, insert: E) {
val index = indexOf(what)
if (index != -1)
add(index, insert)
}
fun <E> MutableList<E>.after(what: E, insert: Collection<E>) {
val index = indexOf(what)
if (index != -1)
addAll(index + 1, insert)
}
fun <E> MutableList<E>.before(what: E, insert: Collection<E>) {
val index = indexOf(what)
if (index != -1)
addAll(index, insert)
}
fun Context.getSyncInterval(interval: Int): String {
val hours = interval / 60 / 60
val minutes = interval / 60 % 60
val hoursText = if (hours > 0)
plural(R.plurals.time_till_hours, hours)
else
null
val minutesText = if (minutes > 0)
plural(R.plurals.time_till_minutes, minutes)
else
""
return hoursText?.plus(" $minutesText") ?: minutesText
}

View File

@ -10,13 +10,11 @@ import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.view.Gravity
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
@ -56,7 +54,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.dialogs.profile.ProfileConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
@ -79,7 +77,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
@ -97,7 +95,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import pl.szczodrzynski.navlib.drawer.NavDrawer
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
import java.io.File
import java.io.IOException
import java.util.*
import kotlin.coroutines.CoroutineContext
@ -110,8 +107,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
const val TAG = "MainActivity"
const val REQUEST_LOGIN_ACTIVITY = 20222
const val DRAWER_PROFILE_ADD_NEW = 200
const val DRAWER_PROFILE_SYNC_ALL = 201
const val DRAWER_PROFILE_EXPORT_DATA = 202
@ -199,7 +194,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.isStatic(true)
.isBelowSeparator(true)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.isInDrawer(true)
.isStatic(true)
@ -257,6 +252,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
val mainSnackbar: MainSnackbar by lazy { MainSnackbar(this) }
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
val requestHandler by lazy { MainActivityRequestHandler(this) }
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
@ -307,6 +303,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val versionBadge = app.buildManager.versionBadge
b.nightlyText.isVisible = versionBadge != null
b.nightlyText.text = versionBadge
if (versionBadge != null) {
b.nightlyText.background.setTintColor(0xa0ff0000.toInt())
}
navLoading = true
@ -382,7 +381,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}
drawerProfileLongClickListener = { _, profile, _, view ->
if (view != null && profile is ProfileDrawerItem) {
showProfileContextMenu(profile, view)
launch {
val appProfile = withContext(Dispatchers.IO) {
App.db.profileDao().getByIdNow(profile.identifier.toInt())
} ?: return@launch
drawer.close()
ProfileConfigDialog(this@MainActivity, appProfile)
}
true
}
else {
@ -462,28 +467,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}
// APP BACKGROUND
if (app.config.ui.appBackground != null) {
try {
app.config.ui.appBackground?.let {
var bg = it
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
if (bgDir.exists()) {
val files = bgDir.listFiles()
val r = Random()
val i = r.nextInt(files.size)
bg = files[i].toString()
}
val linearLayout = b.root
if (bg.endsWith(".gif")) {
linearLayout.background = GifDrawable(bg)
} else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
setAppBackground()
// IT'S WINTER MY DUDES
val today = Date.getToday()
@ -565,7 +549,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
if (App.devMode) {
bottomSheet += BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_debug)
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
}
}
@ -573,7 +557,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
private var profileSettingClickListener = { id: Int, view: View? ->
when (id) {
DRAWER_PROFILE_ADD_NEW -> {
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
requestHandler.requestLogin()
}
DRAWER_PROFILE_SYNC_ALL -> {
EdziennikTask.sync().enqueue(this)
@ -821,7 +805,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
handleIntent(intent?.extras)
}
}
private fun handleIntent(extras: Bundle?) {
fun handleIntent(extras: Bundle?) {
d(TAG, "handleIntent() {")
extras?.keySet()?.forEach { key ->
@ -979,13 +963,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_LOGIN_ACTIVITY) {
if (!app.config.loginFinished)
finish()
else {
handleIntent(data?.extras)
}
}
requestHandler.handleResult(requestCode, resultCode, data)
}
/* _ _ _ _ _
@ -1211,6 +1189,19 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}, 3000)
}
fun setAppBackground() {
try {
b.root.background = app.config.ui.appBackground?.let {
if (it.endsWith(".gif"))
GifDrawable(it)
else
BitmapDrawable.createFromPath(it)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
/* _____ _ _
| __ \ (_) |
| | | |_ __ __ ___ _____ _ __ _| |_ ___ _ __ ___ ___
@ -1286,26 +1277,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
drawer.addProfileSettings(*drawerProfiles.toTypedArray())
}
private fun showProfileContextMenu(profile: IProfile, view: View) {
val profileId = profile.identifier.toInt()
val popupMenu = PopupMenu(this, view)
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
popupMenu.menu.add(0, 2, 2, R.string.profile_menu_remove)
popupMenu.setOnMenuItemClickListener { item ->
if (item.itemId == 1) {
if (profileId != app.profile.id) {
loadProfile(profileId, DRAWER_ITEM_SETTINGS)
return@setOnMenuItemClickListener true
}
loadTarget(DRAWER_ITEM_SETTINGS, null)
} else if (item.itemId == 2) {
ProfileRemoveDialog(this, profileId, profile.name?.getText(this) ?: "?")
}
true
}
popupMenu.show()
}
private val targetPopToHomeList = arrayListOf<Int>()
private var targetHomeId: Int = -1
override fun onBackPressed() {

View File

@ -0,0 +1,220 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-23.
*/
package pl.szczodrzynski.edziennik
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.OpenableColumns
import com.canhub.cropper.CropImage
import com.canhub.cropper.CropImageView
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import java.io.File
import java.io.FileOutputStream
class MainActivityRequestHandler(val activity: MainActivity) {
companion object {
private const val REQUEST_LOGIN_ACTIVITY = 2000
private const val REQUEST_FILE_HEADER_BACKGROUND = 3000
private const val REQUEST_FILE_APP_BACKGROUND = 4000
private const val REQUEST_FILE_PROFILE_IMAGE = 5000
private const val REQUEST_CROP_HEADER_BACKGROUND = 3100
private const val REQUEST_CROP_APP_BACKGROUND = 4100
private const val REQUEST_CROP_PROFILE_IMAGE = 5100
}
private val app = activity.app
private val requestData = mutableMapOf<Int, Any?>()
private val listeners = mutableMapOf<Int, (data: Any?) -> Unit>()
private val manager
get() = app.permissionManager
fun requestLogin() = activity.startActivityForResult(
Intent(activity, LoginActivity::class.java),
REQUEST_LOGIN_ACTIVITY
)
fun requestHeaderBackground(listener: (Any?) -> Unit) =
manager.requestCameraPermission(
activity, 0, isRequired = false
) {
listeners[REQUEST_FILE_HEADER_BACKGROUND] = listener
activity.startActivityForResult(
CropImage.getPickImageChooserIntent(
activity,
activity.getString(R.string.pick_image_intent_chooser_title),
true,
true
),
REQUEST_FILE_HEADER_BACKGROUND
)
}
fun requestAppBackground(listener: (Any?) -> Unit) =
manager.requestCameraPermission(
activity, 0, isRequired = false
) {
listeners[REQUEST_FILE_APP_BACKGROUND] = listener
activity.startActivityForResult(
CropImage.getPickImageChooserIntent(
activity,
activity.getString(R.string.pick_image_intent_chooser_title),
true,
true
),
REQUEST_FILE_APP_BACKGROUND
)
}
fun requestProfileImage(profile: Profile, listener: (Any?) -> Unit) =
manager.requestCameraPermission(
activity, 0, isRequired = false
) {
listeners[REQUEST_FILE_PROFILE_IMAGE] = listener
requestData[REQUEST_FILE_PROFILE_IMAGE] = profile
activity.startActivityForResult(
CropImage.getPickImageChooserIntent(
activity,
activity.getString(R.string.pick_image_intent_chooser_title),
true,
true
),
REQUEST_FILE_PROFILE_IMAGE
)
}
private fun getFileInfo(uri: Uri): Pair<String, String?> {
if (uri.scheme == "file") {
return (uri.lastPathSegment ?: "unknown") to null
}
val cursor = activity.contentResolver.query(
uri,
null,
null,
null,
null,
null
)
return cursor?.use {
if (it.moveToFirst()) {
val name = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
val mimeIndex = it.getColumnIndex("mime_type")
val mimeType = if (mimeIndex != -1) it.getString(mimeIndex) else null
name to mimeType
} else
null
} ?: "unknown" to null
}
private fun shouldCrop(uri: Uri): Boolean {
val (filename, mimeType) = getFileInfo(uri)
return !filename.endsWith(".gif") && mimeType?.endsWith("/gif") != true
}
private fun saveFile(uri: Uri, name: String): String {
val (filename, _) = getFileInfo(uri)
val extension = filename.substringAfterLast('.')
val file = File(activity.filesDir, "$name.$extension")
activity.contentResolver.openInputStream(uri)?.use { input ->
FileOutputStream(file).use { output ->
input.copyTo(output)
}
}
return file.absolutePath
}
fun handleResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode != Activity.RESULT_OK)
return
var uri = CropImage.getPickImageResultUri(activity, data)
when (requestCode) {
REQUEST_LOGIN_ACTIVITY -> {
if (!app.config.loginFinished)
activity.finish()
else {
activity.handleIntent(data?.extras)
}
}
REQUEST_FILE_HEADER_BACKGROUND -> {
if (uri == null)
return // TODO: 2021-03-24 if the app returns no data
if (shouldCrop(uri)) {
val intent = CropImage.activity(uri)
.setAspectRatio(512, 288)
.setGuidelines(CropImageView.Guidelines.ON_TOUCH)
.setAllowFlipping(true)
.setAllowRotation(true)
.setRequestedSize(512, 288)
.getIntent(activity)
activity.startActivityForResult(intent, REQUEST_CROP_HEADER_BACKGROUND)
} else {
val path = saveFile(uri, "header")
app.config.ui.headerBackground = path
listeners.remove(REQUEST_FILE_HEADER_BACKGROUND)?.invoke(path)
}
}
REQUEST_FILE_APP_BACKGROUND -> {
if (uri == null)
return
if (shouldCrop(uri)) {
val intent = CropImage.activity(uri)
.setGuidelines(CropImageView.Guidelines.ON_TOUCH)
.setAllowFlipping(true)
.setAllowRotation(true)
.getIntent(activity)
activity.startActivityForResult(intent, REQUEST_CROP_APP_BACKGROUND)
} else {
val path = saveFile(uri, "background")
app.config.ui.appBackground = path
listeners.remove(REQUEST_FILE_APP_BACKGROUND)?.invoke(path)
}
}
REQUEST_FILE_PROFILE_IMAGE -> {
if (uri == null)
return
if (shouldCrop(uri)) {
val intent = CropImage.activity(uri)
.setAspectRatio(1, 1)
.setCropShape(CropImageView.CropShape.OVAL)
.setGuidelines(CropImageView.Guidelines.ON_TOUCH)
.setAllowFlipping(true)
.setAllowRotation(true)
.setRequestedSize(512, 512)
.getIntent(activity)
activity.startActivityForResult(intent, REQUEST_CROP_PROFILE_IMAGE)
} else {
val profile =
requestData.remove(REQUEST_FILE_PROFILE_IMAGE) as? Profile ?: return
val path = saveFile(uri, "profile${profile.id}")
profile.image = path
listeners.remove(REQUEST_FILE_PROFILE_IMAGE)?.invoke(profile)
}
}
REQUEST_CROP_HEADER_BACKGROUND -> {
uri = CropImage.getActivityResult(data)?.uri ?: return
val path = saveFile(uri, "header")
app.config.ui.headerBackground = path
listeners.remove(REQUEST_FILE_HEADER_BACKGROUND)?.invoke(path)
}
REQUEST_CROP_APP_BACKGROUND -> {
uri = CropImage.getActivityResult(data)?.uri ?: return
val path = saveFile(uri, "background")
app.config.ui.appBackground = path
listeners.remove(REQUEST_FILE_APP_BACKGROUND)?.invoke(path)
}
REQUEST_CROP_PROFILE_IMAGE -> {
uri = CropImage.getActivityResult(data)?.uri ?: return
val profile = requestData.remove(REQUEST_FILE_PROFILE_IMAGE) as? Profile ?: return
val path = saveFile(uri, "profile${profile.id}")
profile.image = path
listeners.remove(REQUEST_FILE_PROFILE_IMAGE)?.invoke(profile)
}
}
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.bell
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class BellSyncConfigDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "BellSyncConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
private fun parse(input: String): Pair<Time, Int>? {
if (input.length < 8) {
return null
}
if (input[2] != ':' || input[5] != ':') {
return null
}
val multiplier = when {
input[0] == '+' -> 1
input[0] == '-' -> -1
else -> return null
}
val time = Time.fromH_m_s("0" + input.substring(1))
return time to multiplier
}
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setView(R.layout.dialog_edit_text)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.reset) { _, _ ->
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
onChangeListener?.invoke()
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val message = dialog.findViewById<TextView>(android.R.id.title)
val editText = dialog.findViewById<TextInputEditText>(android.R.id.text1)
val textLayout = dialog.findViewById<TextInputLayout>(R.id.text_input_layout)
message?.setText(R.string.bell_sync_adjust_content)
editText?.hint = "±H:MM:SS"
editText?.setText(app.config.timetable.bellSyncDiff?.let {
(if (app.config.timetable.bellSyncMultiplier == -1) "-" else "+") + it.stringHMS
} ?: "+0:00:00")
editText?.addTextChangedListener { text ->
val input = text?.toString()
textLayout?.error =
if (input != null && parse(input) == null)
activity.getString(R.string.bell_sync_adjust_error)
else
null
}
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
val input = editText?.text?.toString() ?: return@onClick
val parsed = parse(input)
if (parsed == null) {
Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show()
return@onClick
}
val (time, multiplier) = parsed
app.config.timetable.bellSyncDiff =
if (time.value == 0)
null
else
time
app.config.timetable.bellSyncMultiplier = multiplier
onChangeListener?.invoke()
dialog.dismiss()
}
}}
}

View File

@ -33,7 +33,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationEnableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
@ -64,7 +64,7 @@ class EventManualDialog(
private val app by lazy { activity.application as App }
private lateinit var b: DialogEventManualV2Binding
private lateinit var dialog: AlertDialog
private var profile: Profile? = null
private lateinit var profile: Profile
private var customColor: Int? = null
private val editingShared = editingEvent?.sharedBy != null
@ -80,11 +80,11 @@ class EventManualDialog(
private var progressDialog: AlertDialog? = null
init { run {
init { launch {
if (activity.isFinishing)
return@run
return@launch
onShowListener?.invoke(TAG)
EventBus.getDefault().register(this)
EventBus.getDefault().register(this@EventManualDialog)
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.dialog_event_manual_title)
@ -236,8 +236,15 @@ class EventManualDialog(
progressDialog?.dismiss()
}
private fun loadLists() { launch {
profile = withContext(Dispatchers.Default) { app.db.profileDao().getByIdNow(profileId) }
private fun loadLists() = launch {
val profile = withContext(Dispatchers.Default) {
app.db.profileDao().getByIdNow(profileId)
}
if (profile == null) {
Toast.makeText(activity, R.string.event_manual_no_profile, Toast.LENGTH_SHORT).show()
return@launch
}
this@EventManualDialog.profile = profile
with (b.dateDropdown) {
db = app.db
@ -380,7 +387,7 @@ class EventManualDialog(
})
colorPickerDialog.show(activity.supportFragmentManager, "color-picker-dialog")
}
}}
}
private fun showRemoveEventDialog() {
val shareNotice = when {
@ -417,12 +424,11 @@ class EventManualDialog(
val share = b.shareSwitch.isChecked
if (share && profile?.registration != Profile.REGISTRATION_ENABLED) {
RegistrationEnableDialog(activity, profileId).showEventShareDialog {
if (it != null)
profile = it
if (share && profile.registration != Profile.REGISTRATION_ENABLED) {
RegistrationConfigDialog(activity, profile, onChangeListener = { enabled ->
if (enabled)
saveEvent()
}
}).showEventShareDialog()
return
}

View File

@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext

View File

@ -2,7 +2,7 @@
* Copyright (c) Kacper Ziubryniewicz 2020-1-16
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
package pl.szczodrzynski.edziennik.ui.dialogs.grade
import android.annotation.SuppressLint
import androidx.appcompat.app.AlertDialog

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-23.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.profile
import android.content.res.ColorStateList
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.addTextChangedListener
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.shape.MaterialShapeDrawable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.DialogProfileConfigBinding
import kotlin.coroutines.CoroutineContext
class ProfileConfigDialog(
val activity: MainActivity,
val profile: Profile,
val onProfileSaved: ((profile: Profile) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "ProfileConfigDialog"
}
private lateinit var app: App
private lateinit var b: DialogProfileConfigBinding
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
private var profileChanged = false
private var profileRemoved = false
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogProfileConfigBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
if (!profileRemoved && profileChanged) {
app.profileSave(profile)
onProfileSaved?.invoke(profile)
}
onDismissListener?.invoke(TAG)
}
.show()
b.profile = profile
profile.applyImageTo(b.image)
// I can't believe how simple it is to get the dialog's background color !!
val shape = MaterialShapeDrawable(activity, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents)
val surface = MaterialColors.getColor(activity, R.attr.colorSurface, TAG)
shape.setCornerSize(18.dp.toFloat())
shape.initializeElevationOverlay(activity)
shape.fillColor = ColorStateList.valueOf(surface)
shape.elevation = 16.dp.toFloat()
b.circleView.background = shape
b.nameEdit.addTextChangedListener {
profileChanged = true
}
b.syncSwitch.onChange { _, _ ->
profileChanged = true
}
b.imageButton.onClick {
activity.requestHandler.requestProfileImage(profile) {
val profile = it as? Profile ?: return@requestProfileImage
if (this@ProfileConfigDialog.profile == profile) {
profileChanged = true
b.profile = profile
b.image.setImageDrawable(profile.getImageDrawable(activity))
}
}
}
b.logoutButton.onClick {
ProfileRemoveDialog(activity, profile.id, profile.name) {
profileRemoved = true
dialog.dismiss()
}
}
}}
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-11-13.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
package pl.szczodrzynski.edziennik.ui.dialogs.profile
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
@ -18,7 +18,8 @@ class ProfileRemoveDialog(
val activity: MainActivity,
val profileId: Int,
val profileName: String,
val noProfileRemoval: Boolean = false
val noProfileRemoval: Boolean = false,
val onRemove: (() -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "ProfileRemoveDialog"
@ -95,5 +96,6 @@ class ProfileRemoveDialog(
dialog.dismiss()
activity.reloadTarget()
Toast.makeText(activity, R.string.dialog_profile_remove_success, Toast.LENGTH_LONG).show()
onRemove?.invoke()
}}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-19.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import kotlin.coroutines.CoroutineContext
class AppLanguageDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "AppLanguageDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val languages = mapOf(
null to R.string.language_system,
"pl" to R.string.language_polish,
"en" to R.string.language_english,
"de" to R.string.language_german
)
val languageIds = languages.map { it.key }
val languageNames = languages.map {
activity.getString(it.value)
}
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.app_language_dialog_title)
//.setMessage(R.string.settings_about_language_dialog_text)
.setSingleChoiceItems(
languageNames.toTypedArray(),
languageIds.indexOf(app.config.ui.language),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
app.config.ui.language = languageIds[which]
activity.recreate()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_NOTIFICATIONS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_SETTINGS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R
import kotlin.coroutines.CoroutineContext
class MiniMenuConfigDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "MiniMenuConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val buttons = mapOf(
DRAWER_ITEM_HOME to R.string.menu_home_page,
DRAWER_ITEM_TIMETABLE to R.string.menu_timetable,
DRAWER_ITEM_AGENDA to R.string.menu_agenda,
DRAWER_ITEM_GRADES to R.string.menu_grades,
DRAWER_ITEM_MESSAGES to R.string.menu_messages,
DRAWER_ITEM_HOMEWORK to R.string.menu_homework,
DRAWER_ITEM_BEHAVIOUR to R.string.menu_notices,
DRAWER_ITEM_ATTENDANCE to R.string.menu_attendance,
DRAWER_ITEM_ANNOUNCEMENTS to R.string.menu_announcements,
DRAWER_ITEM_NOTIFICATIONS to R.string.menu_notifications,
DRAWER_ITEM_SETTINGS to R.string.menu_settings
)
val miniMenuButtons = app.config.ui.miniMenuButtons
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_theme_mini_drawer_buttons_dialog_title)
//.setMessage(R.string.settings_theme_mini_drawer_buttons_dialog_text)
.setMultiChoiceItems(
buttons.map { activity.getString(it.value) }.toTypedArray(),
buttons.map { it.key in miniMenuButtons }.toBooleanArray(),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
app.config.ui.miniMenuButtons =
buttons.keys.mapIndexedNotNull { index, id ->
if (dialog.listView.checkedItemPositions[index])
id
else
null
}
if (activity is MainActivity) {
activity.setDrawerItems()
activity.drawer.updateBadges()
}
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.utils.Themes
import kotlin.coroutines.CoroutineContext
class ThemeChooserDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "ThemeChooserDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_theme_theme_text)
.setSingleChoiceItems(
Themes.getThemeNames(activity).toTypedArray(),
Themes.themeIndex,
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
val theme = Themes.themeList[which]
if (app.config.ui.theme == theme.id)
return@setPositiveButton
app.config.ui.theme = theme.id
Themes.themeIndex = which
activity.recreate()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class QuietHoursConfigDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "QuietHoursConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_sync_quiet_hours_dialog_title)
.setItems(arrayOf(
activity.getString(R.string.settings_sync_quiet_hours_set_beginning),
activity.getString(R.string.settings_sync_quiet_hours_set_end)
)) { dialog, which ->
when (which) {
0 -> configStartTime()
1 -> configEndTime()
}
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
private fun configStartTime() {
onShowListener?.invoke(TAG + "Start")
val time = app.config.sync.quietHoursStart ?: return
val picker = MaterialTimePicker.Builder()
.setTitleText(R.string.settings_sync_quiet_hours_set_beginning)
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(time.hour)
.setMinute(time.minute)
.build()
picker.show(activity.supportFragmentManager, TAG)
picker.addOnPositiveButtonClickListener {
app.config.sync.quietHoursEnabled = true
app.config.sync.quietHoursStart = Time(picker.hour, picker.minute, 0)
onChangeListener?.invoke()
}
picker.addOnDismissListener {
onDismissListener?.invoke(TAG + "Start")
}
}
private fun configEndTime() {
onShowListener?.invoke(TAG + "End")
val time = app.config.sync.quietHoursEnd ?: return
val picker = MaterialTimePicker.Builder()
.setTitleText(R.string.settings_sync_quiet_hours_set_end)
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(time.hour)
.setMinute(time.minute)
.build()
picker.show(activity.supportFragmentManager, TAG)
picker.addOnPositiveButtonClickListener {
app.config.sync.quietHoursEnabled = true
app.config.sync.quietHoursEnd = Time(picker.hour, picker.minute, 0)
onChangeListener?.invoke()
}
picker.addOnDismissListener {
onDismissListener?.invoke(TAG + "End")
}
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.AppSync
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import kotlin.coroutines.CoroutineContext
class RegistrationConfigDialog(
val activity: AppCompatActivity,
val profile: Profile,
val onChangeListener: ((enabled: Boolean) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "RegistrationEnableDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
app = activity.applicationContext as App
}}
fun showEventShareDialog() {
onShowListener?.invoke(TAG + "EventShare")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_event_sharing_title)
.setMessage(R.string.registration_config_event_sharing_text)
.setPositiveButton(R.string.i_agree) { _, _ ->
enableRegistration()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "EventShare")
}
.show()
}
fun showEnableDialog() {
onShowListener?.invoke(TAG + "Enable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_config_enable_text)))
.setPositiveButton(R.string.i_agree) { _, _ ->
enableRegistration()
}
.setNegativeButton(R.string.i_disagree, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Enable")
}
.show()
}
fun showDisableDialog() {
onShowListener?.invoke(TAG + "Disable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_config_disable_text)))
.setPositiveButton(R.string.ok) { _, _ ->
disableRegistration()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Disable")
}
.show()
}
private fun enableRegistration() = launch {
onShowListener?.invoke(TAG + "Enabling")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_config_enable_progress_text)
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Enabling")
}
.show()
withContext(Dispatchers.Default) {
profile.registration = Profile.REGISTRATION_ENABLED
// force full registration of the user
App.config.getFor(profile.id).hash = ""
SzkolnyApi(app).runCatching(activity) {
AppSync(app, mutableListOf(), listOf(profile), this).run(
0L,
markAsSeen = true
)
}
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
}
dialog.dismiss()
onChangeListener?.invoke(true)
}
private fun disableRegistration() = launch {
onShowListener?.invoke(TAG + "Disabling")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_config_disable_progress_text)
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Disabling")
}
.show()
withContext(Dispatchers.Default) {
profile.registration = Profile.REGISTRATION_DISABLED
SzkolnyApi(app).runCatching(activity) {
unregisterAppUser(profile.userCode)
}
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
}
dialog.dismiss()
onChangeListener?.invoke(false)
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.AppSync
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import kotlin.coroutines.CoroutineContext
class RegistrationEnableDialog(
val activity: AppCompatActivity,
val profileId: Int
) : CoroutineScope {
companion object {
private const val TAG = "RegistrationEnableDialog"
}
private lateinit var app: App
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
private var progressDialog: AlertDialog? = null
init { run {
if (activity.isFinishing)
return@run
app = activity.applicationContext as App
}}
fun showEventShareDialog(onSuccess: (profile: Profile?) -> Unit) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_manual_need_registration_title)
.setMessage(R.string.event_manual_need_registration_text)
.setPositiveButton(R.string.ok) { dialog, which ->
enableRegistration(onSuccess)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
fun showEnableDialog(onSuccess: (profile: Profile?) -> Unit) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_enable_dialog_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_enable_dialog_text)))
.setPositiveButton(R.string.ok) { dialog, which ->
enableRegistration(onSuccess)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
private fun enableRegistration(onSuccess: (profile: Profile?) -> Unit) { launch {
progressDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_enable_progress_text)
.setCancelable(false)
.show()
val profile = withContext(Dispatchers.Default) {
val profile = app.db.profileDao().getByIdNow(profileId) ?: return@withContext null
profile.registration = Profile.REGISTRATION_ENABLED
// force full registration of the user
App.config.getFor(profile.id).hash = ""
AppSync(app, mutableListOf(), listOf(profile), SzkolnyApi(app)).run(0L, markAsSeen = true)
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
return@withContext profile
}
progressDialog?.dismiss()
onSuccess(profile)
}}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import kotlin.coroutines.CoroutineContext
class SyncIntervalDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "SyncIntervalDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val intervals = listOf(
30 * MINUTE,
45 * MINUTE,
60 * MINUTE,
90 * MINUTE,
2 * HOUR,
3 * HOUR,
4 * HOUR,
6 * HOUR,
10 * HOUR
)
val intervalNames = intervals.map {
activity.getSyncInterval(it.toInt())
}
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_sync_sync_interval_dialog_title)
//.setMessage(R.string.settings_sync_sync_interval_dialog_text)
.setSingleChoiceItems(
intervalNames.toTypedArray(),
intervals.indexOf(app.config.sync.interval.toLong()),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
val interval = intervals[which]
app.config.sync.interval = interval.toInt()
onChangeListener?.invoke()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -16,7 +16,7 @@ import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.dialogs.profile.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
import pl.szczodrzynski.fslogin.decode

View File

@ -23,7 +23,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemStatsBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
import java.text.DecimalFormat

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.view.View
import com.danielstone.materialaboutlibrary.holders.MaterialAboutItemViewHolder
import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem
class MaterialAboutProfileItem(item: MaterialAboutTitleItem) : MaterialAboutTitleItem(item) {
companion object {
fun getViewHolder(view: View): MaterialAboutItemViewHolder =
MaterialAboutTitleItem.getViewHolder(view)
fun setupItem(
holder: MaterialAboutTitleItemViewHolder,
item: MaterialAboutProfileItem,
context: Context
) = MaterialAboutTitleItem.setupItem(holder, item, context)
}
override fun getType(): Int {
return SettingsViewTypeManager.ItemType.PROFILE_ITEM
}
override fun getDetailString() = "MaterialAboutProfileItem{" +
"text=" + text +
", textRes=" + textRes +
", desc=" + desc +
", descRes=" + descRes +
", icon=" + icon +
", iconRes=" + iconRes +
", onClickAction=" + onClickAction +
", onLongClickAction=" + onLongClickAction +
'}'
override fun clone() = MaterialAboutProfileItem(this)
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
abstract class SettingsCard(
protected val util: SettingsUtil,
) {
protected val app: App = util.activity.application as App
protected val activity: MainActivity = util.activity
protected val configGlobal by lazy { app.config }
protected val configProfile by lazy { app.config.forProfile() }
val card by lazy {
buildCard()
}
protected abstract fun buildCard(): MaterialAboutCard
protected abstract fun getItems(): List<MaterialAboutItem>
protected open fun getItemsMore(): List<MaterialAboutItem> = listOf()
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-16.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.danielstone.materialaboutlibrary.MaterialAboutFragment
import com.danielstone.materialaboutlibrary.model.MaterialAboutList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.ui.modules.settings.cards.*
import kotlin.coroutines.CoroutineContext
class SettingsFragment : MaterialAboutFragment(), CoroutineScope {
companion object {
private const val TAG = "SettingsFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val util by lazy {
SettingsUtil(activity) {
refreshMaterialAboutList()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
activity = (getActivity() as MainActivity?) ?: return null
app = activity.application as App
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun getViewTypeManager() =
SettingsViewTypeManager()
override fun getMaterialAboutList(activityContext: Context?): MaterialAboutList {
return MaterialAboutList(
SettingsProfileCard(util).card,
SettingsThemeCard(util).card,
SettingsSyncCard(util).card,
SettingsRegisterCard(util).card,
SettingsAboutCard(util).card,
)
}
}

View File

@ -3,8 +3,8 @@ package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.net.Uri
import android.os.Bundle
import com.danielstone.materialaboutlibrary.ConvenienceBuilder
import com.danielstone.materialaboutlibrary.ConvenienceBuilder.createLicenseCard
import com.danielstone.materialaboutlibrary.MaterialAboutActivity
import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
@ -14,169 +14,374 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.resolveColor
import pl.szczodrzynski.edziennik.utils.Themes
class SettingsLicenseActivity : MaterialAboutActivity() {
var foregroundColor: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
val app = application as App
setTheme(Themes.appTheme)
foregroundColor = Themes.getPrimaryTextColor(this)
super.onCreate(savedInstanceState)
}
private fun createLicenseCard(
context: Context,
libraryTitle: CharSequence,
copyrightYear: CharSequence,
copyrightName: CharSequence,
license: OpenSourceLicense,
libraryUrl: String): MaterialAboutCard {
val licenseItem = MaterialAboutActionItem.Builder()
.icon(IconicsDrawable(this).apply {
private val icon
get() = IconicsDrawable(this).apply {
icon = CommunityMaterial.Icon.cmd_book_outline
colorInt = foregroundColor
sizeDp = 18
})
.setIconGravity(MaterialAboutActionItem.GRAVITY_TOP)
.text(libraryTitle)
.subText(String.format(getString(license.resourceId), copyrightYear, copyrightName))
.setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(context, Uri.parse(libraryUrl)))
.build()
return MaterialAboutCard.Builder().addItem(licenseItem).build()
}
override fun getMaterialAboutList(context: Context): MaterialAboutList {
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(
if (Themes.isDark)
R.style.Theme_MaterialComponents
else
R.style.Theme_MaterialComponents_Light
)
foregroundColor = if (Themes.isDark)
R.color.primaryTextDark.resolveColor(this)
else
R.color.primaryTextLight.resolveColor(this)
super.onCreate(savedInstanceState)
}
return MaterialAboutList(
createLicenseCard(this,
"OkHttp",
private fun license(
title: String,
year: String,
copyright: String,
license: OpenSourceLicense,
url: String
): MaterialAboutCard {
return createLicenseCard(this, icon, title, year, copyright, license).also {
(it.items[0] as MaterialAboutActionItem).onClickAction =
ConvenienceBuilder.createWebsiteOnClickAction(
this,
Uri.parse(url)
)
}
}
override fun getMaterialAboutList(context: Context) = MaterialAboutList(
license(
"Kotlin",
"2000-2020",
"JetBrains s.r.o. and Kotlin Programming Language contributors.",
OpenSourceLicense.APACHE_2,
"https://github.com/JetBrains/kotlin"
),
license(
"Android Jetpack",
"",
"square",
"The Android Open Source Project",
OpenSourceLicense.APACHE_2,
"https://github.com/square/okhttp/"),
createLicenseCard(this,
"MHttp",
"2018",
"Mot.",
"https://github.com/androidx/androidx"
),
license(
"Material Components for Android",
"2014-2020",
"Google, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/motcwang/MHttp/"),
createLicenseCard(this,
"https://github.com/material-components/material-components-android"
),
license(
"OkHttp",
"2019",
"Square, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/square/okhttp"
),
license(
"Retrofit",
"2013",
"Square, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/square/retrofit"
),
license(
"Gson",
"2008",
"Google Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/google/gson"
),
license(
"jsoup",
"2009-2021",
"Jonathan Hedley",
OpenSourceLicense.MIT,
"https://github.com/jhy/jsoup"
),
license(
"jspoon",
"2017",
"Droids On Roids",
OpenSourceLicense.MIT,
"https://github.com/DroidsOnRoids/jspoon"
),
license(
"AgendaCalendarView",
"2015",
"Thibault Guégan",
OpenSourceLicense.APACHE_2,
"https://github.com/Tibolte/AgendaCalendarView/"),
createLicenseCard(this,
"Material Calendar View",
"https://github.com/szkolny-eu/agendacalendarview"
),
license(
"CafeBar",
"2017",
"Dani Mahardhika",
OpenSourceLicense.APACHE_2,
"https://github.com/szkolny-eu/cafebar"
),
license(
"FSLogin",
"2021",
"kuba2k2",
OpenSourceLicense.MIT,
"https://github.com/szkolny-eu/FSLogin"
),
license(
"material-about-library",
"2016-2020",
"Daniel Stone",
OpenSourceLicense.APACHE_2,
"https://github.com/szkolny-eu/material-about-library"
),
license(
"MHttp",
"2018",
"Mot.",
OpenSourceLicense.APACHE_2,
"https://github.com/szkolny-eu/mhttp"
),
license(
"Nachos for Android",
"2016",
"Hootsuite Media, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/szkolny-eu/nachos"
),
license(
"Material Number Sliding Picker",
"2019",
"Alessandro Crugnola",
OpenSourceLicense.MIT,
"https://github.com/kuba2k2/NumberSlidingPicker"
),
license(
"RecyclerTabLayout",
"2017",
"nshmura",
OpenSourceLicense.APACHE_2,
"https://github.com/kuba2k2/RecyclerTabLayout"
),
license(
"Tachyon",
"2019",
"LinkedIn Corporation",
OpenSourceLicense.BSD,
"https://github.com/kuba2k2/Tachyon"
),
license(
"Android-Iconics",
"2021",
"Mike Penz",
OpenSourceLicense.APACHE_2,
"https://github.com/mikepenz/Android-Iconics"
),
license(
"Custom Activity On Crash library",
"2020",
"Eduard Ereza Martínez",
OpenSourceLicense.APACHE_2,
"https://github.com/Ereza/CustomActivityOnCrash"
),
license(
"Material-Calendar-View",
"2017",
"Applandeo sp. z o.o.",
OpenSourceLicense.APACHE_2,
"https://github.com/Applandeo/Material-Calendar-View/"),
createLicenseCard(this,
"Custom Activity On Crash",
"",
"Eduard Ereza MartĂ­nez (Ereza)",
OpenSourceLicense.APACHE_2,
"https://github.com/Ereza/CustomActivityOnCrash/"),
createLicenseCard(this,
"Android-Iconics",
"2018",
"Mike Penz",
OpenSourceLicense.APACHE_2,
"https://github.com/mikepenz/Android-Iconics/"),
createLicenseCard(this,
"MaterialDrawer",
"2016",
"Mike Penz",
OpenSourceLicense.APACHE_2,
"https://github.com/mikepenz/MaterialDrawer/"),
createLicenseCard(this,
"Material Dialogs",
"2014-2016",
"Aidan Michael Follestad",
OpenSourceLicense.MIT,
"https://github.com/afollestad/material-dialogs/"),
createLicenseCard(this,
"MaterialDateTimePicker",
"2014",
"Wouter Dullaert",
OpenSourceLicense.APACHE_2,
"https://github.com/wdullaer/MaterialDateTimePicker/"),
createLicenseCard(this,
"ColorPicker",
"2016",
"Jared Rummler, 2015 Daniel Nilsson",
OpenSourceLicense.APACHE_2,
"https://github.com/jaredrummler/ColorPicker/"),
createLicenseCard(this,
"material-about-library",
"2016-2018",
"Daniel Stone",
OpenSourceLicense.APACHE_2,
"https://github.com/daniel-stoneuk/material-about-library/"),
createLicenseCard(this,
"material-intro",
"2017",
"Jan Heinrich Reimer",
OpenSourceLicense.MIT,
"https://github.com/heinrichreimer/material-intro/"),
createLicenseCard(this,
"JsonViewer",
"2017",
"smuyyh",
OpenSourceLicense.APACHE_2,
"https://github.com/smuyyh/JsonViewer/"),
createLicenseCard(this,
"ShortcutBadger",
"2014",
"Leo Lin",
OpenSourceLicense.APACHE_2,
"https://github.com/leolin310148/ShortcutBadger/"),
createLicenseCard(this,
"Android Image Cropper",
"2016",
"Arthur Teplitzki, 2013 Edmodo, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/ArthurHub/Android-Image-Cropper/"),
createLicenseCard(this,
"https://github.com/Applandeo/Material-Calendar-View"
),
license(
"Android Swipe Layout",
"2014",
"代码家 (daimajia)",
"代码家",
OpenSourceLicense.MIT,
"https://github.com/daimajia/AndroidSwipeLayout/"),
createLicenseCard(this,
"barcodescanner (ZXing)",
"2014",
"Dushyanth Maguluru",
OpenSourceLicense.APACHE_2,
"https://github.com/dm77/barcodescanner/"),
createLicenseCard(this,
"https://github.com/daimajia/AndroidSwipeLayout"
),
license(
"CircularProgressIndicator",
"2018",
"Anton Kozyriatskyi",
OpenSourceLicense.APACHE_2,
"https://github.com/antonKozyriatskyi/CircularProgressIndicator/")
"https://github.com/antonKozyriatskyi/CircularProgressIndicator"
),
license(
"ChatMessageView",
"2019",
"Tsubasa Nakayama",
OpenSourceLicense.APACHE_2,
"https://github.com/bassaer/ChatMessageView"
),
/*createLicenseCard(this,
"NoNonsense-FilePicker",
"",
"Jonas Kalderstam (spacecowboy)",
OpenSourceLicense.GNU_GPL_3,
"https://github.com/spacecowboy/NoNonsense-FilePicker/")*/
license(
"Android Image Cropper",
"2016 Arthur Teplitzki,",
"2013 Edmodo, Inc.",
OpenSourceLicense.APACHE_2,
"https://github.com/CanHub/Android-Image-Cropper"
),
license(
"Chucker",
"2018-2020 Chucker Team,",
"2017 Jeff Gilfelt",
OpenSourceLicense.APACHE_2,
"https://github.com/ChuckerTeam/chucker"
),
license(
"Android-Snowfall",
"2016",
"JetRadar",
OpenSourceLicense.APACHE_2,
"https://github.com/JetradarMobile/android-snowfall"
),
license(
"UONET+ Request Signer",
"2019",
"Wulkanowy",
OpenSourceLicense.MIT,
"https://github.com/wulkanowy/uonet-request-signer"
),
license(
"material-intro",
"2017",
"Jan Heinrich Reimer",
OpenSourceLicense.MIT,
"https://github.com/heinrichreimer/material-intro"
),
license(
"HyperLog Android",
"2018",
"HyperTrack",
OpenSourceLicense.MIT,
"https://github.com/hypertrack/hyperlog-android"
),
license(
"Color Picker",
"2016 Jared Rummler,",
"2015 Daniel Nilsson",
OpenSourceLicense.APACHE_2,
"https://github.com/jaredrummler/ColorPicker"
),
license(
"PowerPermission",
"2020",
"Qifan Yang",
OpenSourceLicense.APACHE_2,
"https://github.com/underwindfall/PowerPermission"
),
license(
"Material DateTime Picker",
"2015",
"Wouter Dullaert",
OpenSourceLicense.APACHE_2,
"https://github.com/wdullaer/MaterialDateTimePicker"
),
license(
"JsonViewer",
"2017",
"smuyyh",
OpenSourceLicense.APACHE_2,
"https://github.com/smuyyh/JsonViewer"
),
license(
"Coil",
"2021",
"Coil Contributors",
OpenSourceLicense.APACHE_2,
"https://github.com/coil-kt/coil"
),
license(
"Barcode Scanner (ZXing)",
"2014",
"Dushyanth Maguluru",
OpenSourceLicense.APACHE_2,
"https://github.com/dm77/barcodescanner"
),
license(
"AutoFitTextView",
"2014",
"Grantland Chew",
OpenSourceLicense.APACHE_2,
"https://github.com/grantland/android-autofittextview"
),
license(
"ShortcutBadger",
"2014",
"Leo Lin",
OpenSourceLicense.APACHE_2,
"https://github.com/leolin310148/ShortcutBadger"
),
license(
"EventBus",
"2012-2020",
"Markus Junginger, greenrobot",
OpenSourceLicense.APACHE_2,
"https://github.com/greenrobot/EventBus"
),
license(
"android-gif-drawable",
"2013 - present,",
"Karol Wrótniak, Droids on Roids LLC\n",
OpenSourceLicense.MIT,
"https://github.com/koral--/android-gif-drawable"
),
license(
"Android Debug Database",
"2019 Amit Shekhar,",
"2011 Android Open Source Project",
OpenSourceLicense.APACHE_2,
"https://github.com/amitshekhariitbhu/Android-Debug-Database"
)
)
}
override fun getActivityTitle(): CharSequence? {
override fun getActivityTitle(): CharSequence {
return getString(R.string.settings_about_licenses_text)
}
}

View File

@ -0,0 +1,189 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import com.danielstone.materialaboutlibrary.items.*
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
class SettingsUtil(
val activity: MainActivity,
private val onRefresh: () -> Unit
) {
fun refresh() = onRefresh()
private fun IIcon.asDrawable(color: Int? = null, size: Int = 20) =
IconicsDrawable(activity).apply {
icon = this@asDrawable
sizeDp = size
colorInt = color ?: Themes.getPrimaryTextColor(activity)
}
fun createCard(
titleRes: Int?,
items: List<MaterialAboutItem>,
itemsMore: List<MaterialAboutItem>,
backgroundColor: Int? = null,
theme: Int? = null
): MaterialAboutCard {
val card = MaterialAboutCard.Builder()
.title(titleRes ?: 0)
.cardColor(backgroundColor ?: 0)
.theme(theme ?: 0)
.build()
card.items.addAll(items)
if (itemsMore.isNotEmpty()) {
card.items.add(createMoreItem(card, itemsMore))
}
return card
}
fun createMoreItem(
card: MaterialAboutCard,
items: List<MaterialAboutItem>
): MaterialAboutActionItem {
val iconColor = card.cardColor.let {
if (it == 0)
null
else
Colors.legibleTextColor(it)
}
val moreItem = MaterialAboutActionItem.Builder()
.text(R.string.settings_more_text)
.icon(CommunityMaterial.Icon.cmd_chevron_down.asDrawable(iconColor, size = 14))
.build()
moreItem.setOnClickAction {
card.items.after(moreItem, items)
card.items.remove(moreItem)
onRefresh()
}
return moreItem
}
fun createSectionItem(text: Int) = MaterialAboutSectionItem(text)
fun createActionItem(
text: Int,
subText: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
onClick: (item: MaterialAboutActionItem) -> Unit
): MaterialAboutActionItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutActionItem.Builder()
.text(text)
.subText(subText ?: 0)
.icon(icon.asDrawable(iconColor))
.build()
item.setOnClickAction {
onClick(item)
}
return item
}
fun createPropertyItem(
text: Int,
subText: Int? = null,
subTextChecked: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
value: Boolean,
beforeChange: ((item: MaterialAboutSwitchItem, value: Boolean) -> Boolean)? = null,
onChange: (item: MaterialAboutSwitchItem, value: Boolean) -> Unit
): MaterialAboutSwitchItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutSwitchItem.Builder()
.text(text)
.subText(subText ?: 0)
.subTextChecked(subTextChecked ?: 0)
.icon(icon.asDrawable(iconColor))
.setChecked(value)
.build()
item.setOnCheckedChangedAction { item, isChecked ->
if (beforeChange?.invoke(item as MaterialAboutSwitchItem, isChecked) == false)
return@setOnCheckedChangedAction false
onChange(item as MaterialAboutSwitchItem, isChecked)
true
}
return item
}
fun createPropertyActionItem(
text: Int,
subText: Int? = null,
subTextChecked: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
value: Boolean,
onChange: (item: MaterialAboutActionSwitchItem, value: Boolean) -> Unit,
onClick: (item: MaterialAboutActionSwitchItem) -> Unit
): MaterialAboutSwitchItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutActionSwitchItem.Builder()
.text(text)
.subText(subText ?: 0)
.subTextChecked(subTextChecked ?: 0)
.icon(icon.asDrawable(iconColor))
.setChecked(value)
.build()
item.setOnClickAction {
onClick(item)
}
item.setOnCheckedChangedAction { item, isChecked ->
onChange(item as MaterialAboutActionSwitchItem, isChecked)
true
}
return item
}
fun createTitleItem(): MaterialAboutTitleItem =
MaterialAboutTitleItem.Builder()
.text(R.string.app_name)
.desc(R.string.settings_about_title_subtext)
.icon(R.mipmap.ic_splash)
.build()
fun createProfileItem(
profile: Profile,
onClick: (item: MaterialAboutProfileItem, profile: Profile) -> Unit
): MaterialAboutProfileItem {
val item = MaterialAboutProfileItem(
MaterialAboutTitleItem.Builder()
.text(profile.name)
.desc(profile.subname)
.icon(profile.getImageDrawable(activity))
.build()
)
item.setOnClickAction {
onClick(item, profile)
}
return item
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.view.View
import com.danielstone.materialaboutlibrary.holders.MaterialAboutItemViewHolder
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem.MaterialAboutTitleItemViewHolder
import com.danielstone.materialaboutlibrary.util.DefaultViewTypeManager
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsViewTypeManager.ItemType.Companion.PROFILE_ITEM
class SettingsViewTypeManager : DefaultViewTypeManager() {
class ItemType {
companion object {
const val PROFILE_ITEM = 10
}
}
override fun getLayout(itemType: Int) = when (itemType) {
PROFILE_ITEM -> R.layout.mal_material_about_profile_item
else -> super.getLayout(itemType)
}
override fun getViewHolder(itemType: Int, view: View): MaterialAboutItemViewHolder =
when (itemType) {
PROFILE_ITEM -> MaterialAboutProfileItem.getViewHolder(view)
else -> super.getViewHolder(itemType, view)
}
override fun setupItem(
itemType: Int,
holder: MaterialAboutItemViewHolder,
item: MaterialAboutItem,
context: Context
) = when (itemType) {
PROFILE_ITEM -> MaterialAboutProfileItem.setupItem(
holder as MaterialAboutTitleItemViewHolder,
item as MaterialAboutProfileItem, context
)
else -> super.setupItem(itemType, holder, item, context)
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import android.media.MediaPlayer
import android.widget.Toast
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsLicenseActivity
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.Utils
import kotlin.coroutines.CoroutineContext
class SettingsAboutCard(util: SettingsUtil) : SettingsCard(util), CoroutineScope {
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var clickCounter = 0
private val mediaPlayer by lazy {
MediaPlayer.create(activity, R.raw.ogarnij_sie)
}
override fun buildCard(): MaterialAboutCard =
util.createCard(
null,
items = listOf(),
itemsMore = listOf(),
backgroundColor = 0xff1976d2.toInt(),
theme = R.style.AppTheme_Dark
).also {
it.items.addAll(getItems(it))
}
override fun getItems() = listOf<MaterialAboutItem>()
override fun getItemsMore() = listOf<MaterialAboutItem>()
private fun getItems(card: MaterialAboutCard) = listOf(
util.createTitleItem(),
util.createActionItem(
text = R.string.settings_about_version_text,
icon = CommunityMaterial.Icon2.cmd_information_outline,
onClick = { item ->
clickCounter++
if (clickCounter < 7)
Toast.makeText(activity, "\uD83D\uDE02", Toast.LENGTH_SHORT).show()
item.subText =
BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE + " \uD83D\uDCA3"
util.refresh()
if (clickCounter >= 7) {
mediaPlayer.start()
clickCounter = 0
}
}
).also {
it.subText = BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE
},
util.createMoreItem(card, items = listOf(
util.createActionItem(
text = R.string.settings_about_changelog_text,
icon = CommunityMaterial.Icon3.cmd_radar
) {
ChangelogDialog(activity)
},
util.createActionItem(
text = R.string.settings_about_update_text,
subText = R.string.settings_about_update_subtext,
icon = CommunityMaterial.Icon3.cmd_update
) {
launch {
UpdateWorker.runNow(app)
}
}
)),
util.createSectionItem(
text = R.string.see_also
),
util.createActionItem(
text = R.string.settings_about_privacy_policy_text,
icon = CommunityMaterial.Icon3.cmd_shield_outline
) {
Utils.openUrl(activity, "https://szkolny.eu/privacy-policy")
},
util.createActionItem(
text = R.string.settings_about_discord_text,
subText = R.string.settings_about_discord_subtext,
icon = SzkolnyFont.Icon.szf_discord_outline
) {
Utils.openUrl(activity, "https://szkolny.eu/discord")
},
util.createActionItem(
text = R.string.settings_about_github_text,
subText = R.string.settings_about_github_subtext,
icon = SzkolnyFont.Icon.szf_github_face
) {
Utils.openUrl(activity, "https://szkolny.eu/github/android")
},
util.createMoreItem(card, items = listOfNotNull(
util.createActionItem(
text = R.string.settings_about_homepage_text,
subText = R.string.settings_about_homepage_subtext,
icon = CommunityMaterial.Icon.cmd_earth
) {
Utils.openUrl(activity, "https://szkolny.eu/")
},
util.createActionItem(
text = R.string.settings_about_licenses_text,
icon = CommunityMaterial.Icon.cmd_code_braces
) {
activity.startActivity(Intent(activity, SettingsLicenseActivity::class.java))
},
if (App.devMode)
util.createActionItem(
text = R.string.settings_about_crash_text,
subText = R.string.settings_about_crash_subtext,
icon = CommunityMaterial.Icon.cmd_bug_outline
) {
throw RuntimeException("MANUAL CRASH")
}
else
null
))
)
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.profile.ProfileConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.settings.MaterialAboutProfileItem
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
class SettingsProfileCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
null,
items = getItems(),
itemsMore = listOf()
)
private fun getProfileItem(): MaterialAboutProfileItem = util.createProfileItem(
profile = app.profile
) { item, profile ->
ProfileConfigDialog(activity, profile, onProfileSaved = {
val index = card.items.indexOf(item)
if (index == -1)
return@ProfileConfigDialog
card.items.remove(item)
card.items.add(index, getProfileItem())
util.refresh()
})
}
override fun getItems() = listOf(
getProfileItem(),
util.createActionItem(
text = R.string.settings_add_student_text,
subText = R.string.settings_add_student_subtext,
icon = CommunityMaterial.Icon.cmd_account_plus_outline
) {
activity.startActivity(Intent(activity, LoginActivity::class.java))
}
)
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_register_title,
items = getItems(),
itemsMore = getItemsMore()
)
private fun getBellSync() =
configGlobal.timetable.bellSyncDiff?.let {
activity.getString(
R.string.settings_register_bell_sync_subtext_format,
(if (configGlobal.timetable.bellSyncMultiplier == -1) "-" else "+") + it.stringHMS
)
} ?: activity.getString(R.string.settings_register_bell_sync_subtext_disabled)
private val sharedEventsItem by lazy {
util.createPropertyItem(
text = R.string.settings_register_shared_events_text,
subText = R.string.settings_register_shared_events_subtext,
icon = CommunityMaterial.Icon3.cmd_share_outline,
value = app.profile.enableSharedEvents
) { _, value ->
app.profile.enableSharedEvents = value
app.profileSave()
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_sharing)
.setMessage(
if (value)
R.string.settings_register_shared_events_dialog_enabled_text
else
R.string.settings_register_shared_events_dialog_disabled_text
)
.setPositiveButton(R.string.ok, null)
.show()
}
}
override fun getItems() = listOfNotNull(
util.createActionItem(
text = R.string.menu_grades_config,
icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline
) {
GradesConfigDialog(activity, reloadOnDismiss = false)
},
util.createActionItem(
text = R.string.menu_attendance_config,
icon = CommunityMaterial.Icon.cmd_calendar_remove_outline
) {
AttendanceConfigDialog(activity, reloadOnDismiss = false)
},
util.createPropertyItem(
text = R.string.settings_register_allow_registration_text,
subText = R.string.settings_register_allow_registration_subtext,
icon = CommunityMaterial.Icon.cmd_account_circle_outline,
value = app.profile.registration == REGISTRATION_ENABLED,
beforeChange = { item, value ->
if (app.profile.registration == REGISTRATION_ENABLED == value)
// allow the switch to change - needed for util.refresh() to change the visual state
return@createPropertyItem true
val dialog =
RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
if (item.isChecked == enabled)
return@RegistrationConfigDialog
item.isChecked = enabled
if (value) {
card.items.after(item, sharedEventsItem)
} else {
card.items.remove(sharedEventsItem)
}
util.refresh()
})
if (value)
dialog.showEnableDialog()
else
dialog.showDisableDialog()
false
}
) { _, _ -> },
if (app.profile.registration == REGISTRATION_ENABLED)
sharedEventsItem
else
null
)
override fun getItemsMore() = listOfNotNull(
util.createActionItem(
text = R.string.settings_register_bell_sync_text,
icon = SzkolnyFont.Icon.szf_alarm_bell_outline,
onClick = {
BellSyncConfigDialog(activity, onChangeListener = {
it.subText = getBellSync()
util.refresh()
})
}
).also {
it.subText = getBellSync()
},
util.createPropertyItem(
text = R.string.settings_register_count_in_seconds_text,
subText = R.string.settings_register_count_in_seconds_subtext,
icon = CommunityMaterial.Icon3.cmd_timer_outline,
value = configGlobal.timetable.countInSeconds
) { _, it ->
configGlobal.timetable.countInSeconds = it
},
if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS)
util.createPropertyItem(
text = R.string.settings_register_show_teacher_absences_text,
icon = CommunityMaterial.Icon.cmd_account_arrow_right_outline,
value = app.profile.getStudentData("showTeacherAbsences", true)
) { _, it ->
app.profile.putStudentData("showTeacherAbsences", it)
app.profileSave()
}
else
null,
util.createPropertyItem(
text = R.string.settings_register_hide_sticks_from_old,
icon = CommunityMaterial.Icon3.cmd_numeric_1_box_outline,
value = configProfile.grades.hideSticksFromOld
) { _, it ->
configProfile.grades.hideSticksFromOld = it
}
)
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.net.Uri
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import android.provider.Settings
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.getSyncInterval
import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.QuietHoursConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncIntervalDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.models.Time
class SettingsSyncCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_sync_title,
items = getItems(),
itemsMore = getItemsMore()
)
private fun getQuietHours(): String {
if (configGlobal.sync.quietHoursStart == null) {
configGlobal.sync.quietHoursStart = Time(22, 30, 0)
}
if (configGlobal.sync.quietHoursEnd == null) {
configGlobal.sync.quietHoursEnd = Time(6, 30, 0)
}
return activity.getString(
if (configGlobal.sync.quietHoursStart!! > configGlobal.sync.quietHoursEnd!!)
R.string.settings_sync_quiet_hours_subtext_next_day_format
else
R.string.settings_sync_quiet_hours_subtext_format,
configGlobal.sync.quietHoursStart?.stringHM,
configGlobal.sync.quietHoursEnd?.stringHM
)
}
private val syncWifiItem by lazy {
util.createPropertyItem(
text = R.string.settings_sync_wifi_text,
subText = R.string.settings_sync_wifi_subtext,
icon = CommunityMaterial.Icon3.cmd_wifi_strength_2,
value = configGlobal.sync.onlyWifi
) { _, it ->
configGlobal.sync.onlyWifi = it
SyncWorker.rescheduleNext(app)
}
}
override fun getItems() = listOfNotNull(
util.createPropertyActionItem(
text = R.string.settings_sync_sync_interval_text,
subText = R.string.settings_sync_sync_interval_subtext_disabled,
icon = CommunityMaterial.Icon.cmd_download_outline,
value = configGlobal.sync.enabled,
onChange = { item, value ->
// When calling onChange from the onClick listener below
// a list refresh is requested, the adapter refreshes
// all view holders, changing the state of the switch
// view, thus calling onChange again, causing it to
// try to recursively refresh the list, therefore
// crashing the app. To avoid this, the method will
// continue only if the checked state is different
// from the saved value, which should only happen
// when clicking the switch manually or when called
// by onClick, **once** (because onClick doesn't
// update the config value - we let the switch
// listener do it). Then there comes a different problem,
// when onClick changes the subText and the onChange
// listener returns because the boolean value
// is unchanged, leaving the list not refreshed.
// To solve this, a list refresh is also requested
// in onClick, when the config value is the same
// as the new switch value, which would normally
// cause the onChange method to exit here.
if (value == configGlobal.sync.enabled)
return@createPropertyActionItem
if (value) {
card.items.after(item, syncWifiItem)
} else {
card.items.remove(syncWifiItem)
}
util.refresh()
configGlobal.sync.enabled = value
SyncWorker.rescheduleNext(app)
},
onClick = { item ->
SyncIntervalDialog(activity, onChangeListener = {
item.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval)
item.isChecked = true
item.onCheckedChangedAction.onCheckedChanged(item, true)
if (configGlobal.sync.enabled)
util.refresh()
})
}
).also {
it.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval)
},
if (configGlobal.sync.enabled)
syncWifiItem
else
null,
util.createActionItem(
text = R.string.settings_profile_notifications_text,
subText = R.string.settings_profile_notifications_subtext,
icon = CommunityMaterial.Icon2.cmd_filter_outline
) {
NotificationFilterDialog(activity)
},
util.createPropertyActionItem(
text = R.string.settings_sync_quiet_hours_text,
subText = R.string.settings_sync_quiet_hours_subtext_disabled,
icon = CommunityMaterial.Icon.cmd_bell_sleep_outline,
value = configGlobal.sync.quietHoursEnabled,
onChange = { _, value ->
configGlobal.sync.quietHoursEnabled = value
},
onClick = { item ->
QuietHoursConfigDialog(activity, onChangeListener = {
item.subTextChecked = getQuietHours()
item.isChecked = configGlobal.sync.quietHoursEnabled
util.refresh()
})
}
).also {
it.subTextChecked = getQuietHours()
},
util.createActionItem(
text = R.string.settings_sync_web_push_text,
subText = R.string.settings_sync_web_push_subtext,
icon = CommunityMaterial.Icon2.cmd_laptop
) {
activity.loadTarget(MainActivity.TARGET_WEB_PUSH)
}
)
override fun getItemsMore() = listOfNotNull(
util.createPropertyItem(
text = R.string.settings_sync_updates_text,
icon = CommunityMaterial.Icon.cmd_cellphone_arrow_down,
value = configGlobal.sync.notifyAboutUpdates
) { _, it ->
configGlobal.sync.notifyAboutUpdates = it
UpdateWorker.rescheduleNext(app)
},
if (SDK_INT >= VERSION_CODES.KITKAT)
util.createActionItem(
text = R.string.settings_sync_notifications_settings_text,
subText = R.string.settings_sync_notifications_settings_subtext,
icon = CommunityMaterial.Icon.cmd_cog_outline
) {
val channel = app.notificationChannelsManager.data.key
val intent = Intent().apply {
when {
SDK_INT >= VERSION_CODES.O -> {
action = Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS
putExtra(Settings.EXTRA_APP_PACKAGE, app.packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, channel)
addFlags(FLAG_ACTIVITY_NEW_TASK)
}
SDK_INT >= VERSION_CODES.LOLLIPOP -> {
action = "android.settings.APP_NOTIFICATION_SETTINGS"
putExtra("app_package", app.packageName)
putExtra("app_uid", app.applicationInfo.uid)
}
else -> {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
addCategory(Intent.CATEGORY_DEFAULT)
data = Uri.parse("package:" + app.packageName)
}
}
}
activity.startActivity(intent)
}
else
null
)
}

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AppLanguageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MiniMenuConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ThemeChooserDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date
class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_theme_title,
items = getItems(),
itemsMore = getItemsMore()
)
override fun getItems() = listOfNotNull(
if (Date.getToday().month % 11 == 1) // cool math games
util.createPropertyItem(
text = R.string.settings_theme_snowfall_text,
subText = R.string.settings_theme_snowfall_subtext,
icon = CommunityMaterial.Icon3.cmd_snowflake,
value = configGlobal.ui.snowfall
) { _, it ->
configGlobal.ui.snowfall = it
activity.recreate()
}
else null,
util.createActionItem(
text = R.string.settings_theme_theme_text,
subText = Themes.getThemeNameRes(),
icon = CommunityMaterial.Icon3.cmd_palette_outline
) {
ThemeChooserDialog(activity)
},
util.createActionItem(
text = R.string.settings_about_language_text,
subText = R.string.settings_about_language_subtext,
icon = CommunityMaterial.Icon3.cmd_translate
) {
AppLanguageDialog(activity)
},
util.createPropertyItem(
text = R.string.settings_theme_mini_drawer_text,
subText = R.string.settings_theme_mini_drawer_subtext,
icon = CommunityMaterial.Icon.cmd_dots_vertical,
value = configGlobal.ui.miniMenuVisible
) { _, it ->
configGlobal.ui.miniMenuVisible = it
activity.navView.drawer.miniDrawerVisiblePortrait = it
}
)
override fun getItemsMore() = listOf(
util.createActionItem(
text = R.string.settings_theme_mini_drawer_buttons_text,
icon = CommunityMaterial.Icon2.cmd_format_list_checks
) {
MiniMenuConfigDialog(activity)
},
util.createActionItem(
text = R.string.settings_theme_drawer_header_text,
icon = CommunityMaterial.Icon2.cmd_image_outline
) {
if (app.config.ui.appBackground == null) {
setHeaderBackground()
return@createActionItem
}
MaterialAlertDialogBuilder(activity)
.setItems(
arrayOf(
activity.getString(R.string.settings_theme_drawer_header_dialog_set),
activity.getString(R.string.settings_theme_drawer_header_dialog_restore)
)
) { _, which ->
when (which) {
0 -> setHeaderBackground()
1 -> {
app.config.ui.headerBackground = null
activity.drawer.setAccountHeaderBackground(null)
activity.drawer.open()
}
}
}
.setNegativeButton(R.string.cancel, null)
.show()
},
util.createActionItem(
text = R.string.settings_theme_app_background_text,
subText = R.string.settings_theme_app_background_subtext,
icon = CommunityMaterial.Icon2.cmd_image_filter_hdr
) {
if (app.config.ui.appBackground == null) {
setAppBackground()
return@createActionItem
}
MaterialAlertDialogBuilder(activity)
.setItems(
arrayOf(
activity.getString(R.string.settings_theme_app_background_dialog_set),
activity.getString(R.string.settings_theme_app_background_dialog_restore)
)
) { _, which ->
when (which) {
0 -> setAppBackground()
1 -> {
app.config.ui.appBackground = null
activity.setAppBackground()
}
}
}
.setNegativeButton(R.string.cancel, null)
.show()
},
util.createPropertyItem(
text = R.string.settings_theme_open_drawer_on_back_pressed_text,
icon = CommunityMaterial.Icon3.cmd_menu_open,
value = configGlobal.ui.openDrawerOnBackPressed
) { _, it ->
configGlobal.ui.openDrawerOnBackPressed = it
}
)
private fun setHeaderBackground() = activity.requestHandler.requestHeaderBackground {
activity.drawer.setAccountHeaderBackground(null)
activity.drawer.setAccountHeaderBackground(app.config.ui.headerBackground)
activity.drawer.open()
}
private fun setAppBackground() = activity.requestHandler.requestAppBackground {
activity.setAppBackground()
}
}

View File

@ -1,75 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.webpush;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.zxing.Result;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
import pl.szczodrzynski.edziennik.R;
public class QrScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
private ZXingScannerView mScannerView;
public static ZXingScannerView.ResultHandler resultHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
setContentView(mScannerView);
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (result == PackageManager.PERMISSION_GRANTED) {
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera(); // Start camera on resume
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
}
}
@Override
public void onResume() {
super.onResume();
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera(); // Start camera on resume
}
@Override
public void onPause() {
super.onPause();
mScannerView.stopCamera(); // Stop camera on pause
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mScannerView.startCamera();
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_SHORT).show();
}
}
// other 'case' lines to check for other
// permissions this app might request
}
}
@Override
public void handleResult(Result rawResult) {
if (resultHandler != null) {
resultHandler.handleResult(rawResult);
}
finish();
}
}

View File

@ -4,15 +4,11 @@
package pl.szczodrzynski.edziennik.ui.modules.webpush
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
@ -25,7 +21,6 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
import pl.szczodrzynski.edziennik.databinding.WebPushFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.QrScannerDialog
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.Themes
import kotlin.coroutines.CoroutineContext
class WebPushFragment : Fragment(), CoroutineScope {
@ -46,13 +41,13 @@ class WebPushFragment : Fragment(), CoroutineScope {
SzkolnyApi(app)
}
private val manager
get() = app.permissionManager
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
context!!.theme.applyStyle(Themes.appTheme, true)
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false)
// activity, context and profile is valid
b = WebPushFragmentBinding.inflate(inflater)
return b.root
@ -60,19 +55,15 @@ class WebPushFragment : Fragment(), CoroutineScope {
@SuppressLint("DefaultLocale")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// TODO check if app, activity, b can be null
if (app.profile == null || !isAdded)
if (!isAdded)
return
b.scanQrCode.onClick {
val result = ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
if (result == PackageManager.PERMISSION_GRANTED) {
manager.requestCameraPermission(activity, R.string.permissions_qr_scanner) {
QrScannerDialog(activity, {
b.tokenEditText.setText(it.crc32().toString(36).toUpperCase())
pairBrowser(browserId = it)
})
} else {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), 1)
}
}

View File

@ -1,28 +0,0 @@
package pl.szczodrzynski.edziennik.utils;
import android.content.Context;
import android.view.ViewGroup;
import com.google.android.material.snackbar.Snackbar;
import androidx.core.view.ViewCompat;
import pl.szczodrzynski.edziennik.R;
public class SnackbarHelper {
public static void configSnackbar(Context context, Snackbar snack) {
addMargins(snack);
setRoundBordersBg(context, snack);
ViewCompat.setElevation(snack.getView(), 6f);
}
private static void addMargins(Snackbar snack) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) snack.getView().getLayoutParams();
params.setMargins(12, 12, 12, 12);
snack.getView().setLayoutParams(params);
}
private static void setRoundBordersBg(Context context, Snackbar snackbar) {
snackbar.getView().setBackground(context.getResources().getDrawable(R.drawable.bg_snackbar));
}
}

View File

@ -42,6 +42,12 @@ object Themes {
theme = themeList[value]
}
var themeIndex
get() = themeList.indexOf(theme)
set(value) {
theme = themeList[value]
}
val appThemeNoDisplay: Int
get() = if (theme.isDark) R.style.AppTheme_Dark_NoDisplay else R.style.AppTheme_Light_NoDisplay
@ -61,38 +67,15 @@ object Themes {
return getColorFromAttr(context, android.R.attr.textColorSecondary)
}
/*public static int getChipColorRes() {
switch (themeInt) {
case THEME_LIGHT:
return R.color.chipBackgroundLight;
default:
case THEME_DARK:
return R.color.chipBackgroundDark;
case THEME_BLACK:
return R.color.chipBackgroundBlack;
case THEME_CHOCOLATE:
return R.color.chipBackgroundChocolate;
case THEME_BLUE:
return R.color.chipBackgroundBlue;
case THEME_PURPLE:
return R.color.chipBackgroundPurple;
case THEME_GREEN:
return R.color.chipBackgroundGreen;
case THEME_AMBER:
return R.color.chipBackgroundAmber;
case THEME_RED:
return R.color.chipBackgroundRed;
}
}*/
fun getThemeName(context: Context): String {
return context.getString(theme.name)
}
fun getThemeNames(context: Context): List<String> {
val list = mutableListOf<String>()
for (theme in themeList) {
list += context.getString(theme.name)
}
return list
@StringRes
fun getThemeNameRes() = theme.name
fun getThemeNames(context: Context) =
themeList.map {
context.getString(it.name)
}
}

View File

@ -31,31 +31,50 @@ class PermissionManager(val app: App) : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private fun isStoragePermissionGranted() = if (Build.VERSION.SDK_INT >= 23) {
app.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
} else {
private fun isPermissionGranted(name: String) =
if (Build.VERSION.SDK_INT >= 23)
app.checkSelfPermission(name) == PackageManager.PERMISSION_GRANTED
else
true
private fun openPermissionSettings(activity: AppCompatActivity) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", app.packageName, null)
intent.data = uri
activity.startActivity(intent)
}
fun requestStoragePermission(
private fun requestPermission(
activity: AppCompatActivity,
@StringRes permissionMessage: Int,
isRequired: Boolean = true,
permissionName: String,
onSuccess: suspend CoroutineScope.() -> Unit
) {
launch {
if (isStoragePermissionGranted()) {
if (isPermissionGranted(permissionName)) {
onSuccess()
return@launch
}
val result = activity.awaitAskPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
val result = activity.awaitAskPermissions(permissionName)
when {
result.hasAllGranted() -> onSuccess()
result.hasRational() -> {
if (!isRequired) {
onSuccess()
return@launch
}
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.permissions_required)
.setMessage(permissionMessage)
.setPositiveButton(R.string.ok) { _, _ ->
requestStoragePermission(activity, permissionMessage, onSuccess)
requestPermission(
activity,
permissionMessage,
isRequired,
permissionName,
onSuccess
)
}
.setNegativeButton(R.string.cancel, null)
.show()
@ -65,10 +84,7 @@ class PermissionManager(val app: App) : CoroutineScope {
.setTitle(R.string.permissions_required)
.setMessage(R.string.permissions_denied)
.setPositiveButton(R.string.ok) { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", app.packageName, null)
intent.data = uri
activity.startActivity(intent)
openPermissionSettings(activity)
}
.setNegativeButton(R.string.cancel, null)
.show()
@ -76,4 +92,30 @@ class PermissionManager(val app: App) : CoroutineScope {
}
}
}
fun requestStoragePermission(
activity: AppCompatActivity,
@StringRes permissionMessage: Int,
isRequired: Boolean = true,
onSuccess: suspend CoroutineScope.() -> Unit
) = requestPermission(
activity,
permissionMessage,
isRequired,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
onSuccess
)
fun requestCameraPermission(
activity: AppCompatActivity,
@StringRes permissionMessage: Int,
isRequired: Boolean = true,
onSuccess: suspend CoroutineScope.() -> Unit
) = requestPermission(
activity,
permissionMessage,
isRequired,
Manifest.permission.CAMERA,
onSuccess
)
}

View File

@ -5,5 +5,5 @@
<corners android:radius="4dp" />
<stroke android:color="@color/dividerColor" android:width="1dp" />
<solid android:color="#DCDCDC" />
<solid android:color="@color/dividerColor" />
</shape>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp" />
<stroke android:color="@color/dividerColor" android:width="1dp" />
<solid android:color="#DCDCDC" />
</shape>

View File

@ -1,5 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#323232" />
<corners android:radius="4dp" />
</shape>

View File

@ -4,4 +4,5 @@
<corners android:radius="4dp" />
<stroke android:width="1dp" android:color="#1e000000" />
<solid android:color="#00000000" />
</shape>

View File

@ -20,8 +20,7 @@
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="@dimen/customactivityoncrash_activity_vertical_margin"
android:paddingLeft="@dimen/customactivityoncrash_activity_horizontal_margin"
android:paddingRight="@dimen/customactivityoncrash_activity_horizontal_margin"
android:paddingHorizontal="@dimen/customactivityoncrash_activity_horizontal_margin"
android:paddingTop="@dimen/customactivityoncrash_activity_vertical_margin">
<ImageView

View File

@ -1,20 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_margin="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@ -40,13 +40,13 @@
android:layout_marginHorizontal="48dp"
android:layout_marginVertical="8dp"
android:background="@drawable/bg_rounded_4dp"
android:backgroundTint="#a0ff0000"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:padding="4dp"
android:textAllCaps="true"
android:textSize="12sp"
android:textStyle="bold"
tools:backgroundTint="#a0ff0000"
tools:text="Nightly\n20200503" />
</FrameLayout>

View File

@ -13,8 +13,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingEnd="24dp">
android:paddingHorizontal="24dp">
<TextView
style="@style/TextAppearance.AppCompat.Small"
@ -34,7 +33,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
android:paddingBottom="8dp"
android:text="@string/attendance_config_use_symbols_hint"
android:textAppearance="@style/NavView.TextView.Helper"
android:textSize="12sp"

View File

@ -27,9 +27,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp">
android:paddingHorizontal="24dp"
android:paddingTop="24dp">
<LinearLayout
android:layout_width="match_parent"

View File

@ -6,9 +6,7 @@
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="8dp"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
android:clickable="true"
android:focusable="true" />

View File

@ -205,8 +205,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginVertical="4dp"
android:background="@color/dividerColor" />
<LinearLayout
@ -239,8 +238,7 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:visibility="gone"
tools:max="2700"
tools:progress="780"
@ -259,8 +257,7 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginVertical="4dp"
android:background="@color/dividerColor" />
<TextView

View File

@ -53,10 +53,7 @@
android:id="@+id/cardUpdateText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"
android:text="@string/card_update_text_format"
android:textAppearance="@style/TextAppearance.AppCompat.Small"

View File

@ -1,8 +1,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<WebView

View File

@ -14,14 +14,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingEnd="24dp">
android:paddingHorizontal="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="10dp"
android:layout_marginVertical="16dp"
style="@style/TextAppearance.AppCompat.Small"
android:text="@string/grades_config_title"/>
@ -141,8 +139,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:layout_marginVertical="4dp"
style="@style/TextAppearance.AppCompat.Small"
android:text="@string/menu_grades_sort_mode"/>

View File

@ -16,16 +16,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingTop="24dp"
android:paddingRight="16dp">
android:paddingHorizontal="16dp"
android:paddingTop="24dp">
<TextView
android:id="@+id/dayDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"
android:textAppearance="@style/NavView.TextView.Title"
android:textIsSelectable="true"
@ -35,8 +33,7 @@
android:id="@+id/lessonsInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:textIsSelectable="true"
@ -55,10 +52,8 @@
layout="@layout/row_lesson_change_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="5dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="5dp"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
@ -74,10 +69,8 @@
layout="@layout/row_teacher_absence_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
@ -86,10 +79,8 @@
android:id="@+id/eventsNoData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:layout_marginHorizontal="8dp"
android:paddingVertical="16dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) Kuba Szczodrzyński 2021-3-19.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
android:paddingEnd="?attr/dialogPreferredPadding">
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
tools:text="This is a custom message of the dialog." />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_input_layout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@ -27,9 +27,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp">
android:paddingHorizontal="24dp"
android:paddingTop="24dp">
<LinearLayout
android:layout_width="match_parent"

View File

@ -16,10 +16,8 @@
layout="@layout/row_lesson_change_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
@ -28,10 +26,8 @@
layout="@layout/row_teacher_absence_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
@ -39,8 +35,7 @@
android:id="@+id/eventListLessonDetails"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:layout_marginHorizontal="24dp"
android:orientation="vertical">
<TextView
@ -87,10 +82,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
tools:listitem="@layout/row_dialog_event_list_item" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -86,16 +86,14 @@
android:id="@+id/registerEventManualShare"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/dialog_event_manual_share_enabled" />
<TextView
android:id="@+id/registerEventManualShareText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/dialog_event_manual_share_first_notice"
android:visibility="gone"
tools:visibility="visible" />

View File

@ -139,8 +139,7 @@
android:id="@+id/showMore"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingVertical="16dp"
android:text="@string/dialog_event_manual_more_options"
android:background="?selectableItemBackground"

View File

@ -41,9 +41,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp">
android:paddingHorizontal="24dp"
android:paddingTop="24dp">
<LinearLayout
android:layout_width="match_parent"
@ -261,8 +260,7 @@
android:id="@+id/gradeHistoryNest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-8dp"
android:layout_marginRight="-8dp"
android:layout_marginHorizontal="-8dp"
android:visibility="@{historyVisible ? View.VISIBLE : View.GONE}">
<androidx.recyclerview.widget.RecyclerView

View File

@ -6,10 +6,8 @@
android:id="@+id/lessonChangeView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:clipToPadding="false"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp"
tools:listitem="@layout/timetable_lesson" />
</layout>

View File

@ -31,9 +31,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingTop="24dp"
android:paddingRight="16dp">
android:paddingHorizontal="16dp"
android:paddingTop="24dp">
<TextView
android:id="@+id/annotation"
@ -41,8 +40,7 @@
android:layout_height="wrap_content"
android:background="@drawable/timetable_lesson_annotation"
android:fontFamily="sans-serif-condensed"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingHorizontal="8dp"
android:text="@string/timetable_lesson_cancelled"
android:textColor="#000"
android:textSize="12sp"
@ -54,8 +52,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:baselineAligned="false"
android:orientation="horizontal">
@ -133,9 +130,8 @@
android:id="@+id/shiftedLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal">
@ -161,9 +157,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:baselineAligned="false"
android:orientation="horizontal">
@ -233,8 +228,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:baselineAligned="false"
android:orientation="horizontal">
@ -304,10 +298,8 @@
android:id="@+id/eventsNoData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:layout_marginHorizontal="8dp"
android:paddingVertical="16dp"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-3-22.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="pl.szczodrzynski.edziennik.data.db.entity.Profile" />
<variable
name="profile"
type="Profile" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingTop="24dp">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<com.mikepenz.materialdrawer.view.BezelImageView
android:id="@+id/image"
android:layout_width="120dp"
android:layout_height="120dp"
android:scaleType="centerCrop"
tools:src="@drawable/face_1" />
<View
android:id="@+id/circleView"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="bottom|end"
tools:background="?colorSurface" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/imageButtonIcon"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="bottom|end"
app:iiv_color="?colorOnBackground"
app:iiv_icon="szf-image-plus-outline"
app:iiv_padding="5dp"
tools:background="@color/colorSurface_16dp" />
<View
android:id="@+id/imageButton"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="bottom|end"
android:background="?selectableItemBackgroundBorderless" />
</FrameLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/nameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/nameEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/profile_config_name_hint"
android:text="@={profile.name}"
tools:text="Paweł Informatyczny" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_horizontal"
android:textAppearance="@style/NavView.TextView.Subtitle"
android:textColor="?android:textColorSecondary"
android:text="@{profile.subname}"
tools:text="3b3t - 2020/2021" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/syncSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/profile_config_sync_enabled"
android:checked="@={profile.syncEnabled}" />
<com.google.android.material.button.MaterialButton
android:id="@+id/logoutButton"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/profile_config_logout"
android:textColor="@color/md_red_500"
app:rippleColor="@color/md_red_300"
app:strokeColor="@color/md_red_500" />
</LinearLayout>
</ScrollView>
</layout>

View File

@ -36,10 +36,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="16dp"
android:paddingRight="24dp"
android:paddingBottom="16dp">
android:paddingHorizontal="24dp"
android:paddingVertical="16dp">
<TextView
android:id="@+id/title"

View File

@ -6,10 +6,8 @@
android:id="@+id/teacherAbsenceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:clipToPadding="false"
android:paddingHorizontal="24dp"
android:paddingVertical="8dp"
tools:listitem="@layout/row_dialog_teacher_absence_item" />
</layout>

View File

@ -8,9 +8,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingTop="24dp"
android:paddingRight="16dp">
android:paddingHorizontal="16dp"
android:paddingTop="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -90,8 +90,7 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="top"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:visibility="gone"
app:iiv_color="@color/md_green_500"
app:iiv_icon="cmd-check"

View File

@ -21,12 +21,7 @@
android:id="@+id/announcementsNoData"
android:layout_width="wrap_content"
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"
android:layout_marginBottom="8dp"
android:layout_margin="8dp"
android:text="@string/school_notices_no_data"
android:textSize="18sp"
android:textStyle="italic"

View File

@ -59,8 +59,7 @@
android:id="@+id/presentCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -79,8 +78,7 @@
android:id="@+id/absentCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -99,8 +97,7 @@
android:id="@+id/absentUnexcusedCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -119,8 +116,7 @@
android:id="@+id/belatedCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -139,8 +135,7 @@
android:id="@+id/releasedCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="8" />
@ -152,8 +147,7 @@
android:id="@+id/attendancePercentage"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="8dp"
android:layout_weight="1"
app:direction="clockwise"
app:drawDot="false"

View File

@ -17,8 +17,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp">
android:layout_marginHorizontal="8dp">
<TextView
android:id="@+id/noticesSummaryTitle"
@ -43,8 +42,7 @@
android:id="@+id/noticesPraisesCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -63,8 +61,7 @@
android:id="@+id/noticesWarningsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>
@ -83,8 +80,7 @@
android:id="@+id/noticesOtherCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginHorizontal="5dp"
android:textStyle="bold"
tools:text="0" />
</LinearLayout>

View File

@ -11,11 +11,8 @@
android:id="@+id/runLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -33,10 +30,7 @@
android:id="@+id/textView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/debug_notice"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
@ -48,10 +42,7 @@
android:id="@+id/debugRegister"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
style="@style/Widget.MaterialComponents.Button"
android:text="Run"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
@ -69,10 +60,7 @@
android:id="@+id/debugAppconfig"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
style="@style/Widget.MaterialComponents.Button"
android:text="AppConfig"
app:layout_constraintEnd_toStartOf="@+id/guideline3"
@ -90,10 +78,7 @@
android:id="@+id/debugAppprofile"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
style="@style/Widget.MaterialComponents.Button"
android:text="AppProfile"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -18,9 +18,7 @@
android:id="@+id/targetDeviceDropDown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint=""
android:paddingEnd="6.0dip"
android:paddingRight="6.0dip" />
android:hint="" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
@ -32,11 +30,8 @@
android:id="@+id/faqText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:autoLink="all"
android:background="?selectableItemBackground"
android:text="@string/feedback_faq"
@ -48,9 +43,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:text="@string/feedback_faq_button"
android:visibility="gone" />
@ -66,11 +60,8 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/feedback_title"
android:textSize="20sp" />
@ -78,11 +69,8 @@
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:hint="@string/feedback_ask_a_question"
app:errorEnabled="true"
app:hintAnimationEnabled="true"
@ -115,16 +103,14 @@
android:id="@+id/chatLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginVertical="8dp"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/feedback_history"
android:textSize="20sp" />
@ -132,8 +118,7 @@
android:id="@+id/chat_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" />
android:layout_marginVertical="8dp" />
</LinearLayout>
</LinearLayout>

View File

@ -10,9 +10,8 @@
android:id="@+id/subjectName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:textSize="24sp"
android:textAppearance="@style/NavView.TextView.Title"
tools:text="geografia" />
@ -21,9 +20,8 @@
android:id="@+id/semesterName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:textAppearance="@style/NavView.TextView.Subtitle"
tools:text="Semestr 1" />
@ -36,8 +34,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_editor_semester_average_before" />
<TextView
@ -47,8 +44,7 @@
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_rounded_4dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingHorizontal="5dp"
android:textColor="@color/black"
android:textStyle="bold"
tools:text="0.63" />
@ -63,8 +59,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_editor_semester_average_after" />
<TextView
@ -74,8 +69,7 @@
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_rounded_4dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingHorizontal="5dp"
android:textColor="@color/black"
android:textStyle="bold"
tools:text="2.75" />
@ -96,8 +90,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_editor_semester_year_average_before" />
<TextView
@ -107,8 +100,7 @@
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_rounded_4dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingHorizontal="5dp"
android:textColor="@color/black"
android:textStyle="bold"
tools:text="0.63" />
@ -123,8 +115,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_editor_semester_year_average_after" />
<TextView
@ -134,8 +125,7 @@
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/bg_rounded_4dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingHorizontal="5dp"
android:textColor="@color/black"
android:textStyle="bold"
tools:text="2.75" />
@ -146,9 +136,8 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:text="@string/grades_editor_modify_help"
android:textStyle="italic" />
@ -156,9 +145,8 @@
android:id="@+id/addGrade"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:minHeight="0dp"
android:text="@string/grades_editor_add_grade" />
@ -166,9 +154,8 @@
android:id="@+id/restoreGrades"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:minHeight="0dp"
android:text="@string/grades_editor_restore" />

View File

@ -22,9 +22,8 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginHorizontal="40dp"
android:layout_marginTop="20dp"
android:layout_marginRight="40dp"
android:gravity="center"
android:text="@string/profile_empty_error_home_subtitle"
android:textSize="22sp" />

View File

@ -73,9 +73,8 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:text="@string/timetable_not_public_text"
android:textSize="16sp" />
@ -83,9 +82,8 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:gravity="center"
android:text="@string/timetable_not_public_hint"
android:textSize="14sp" />

View File

@ -11,8 +11,7 @@
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp">
android:paddingVertical="8dp">
<pl.szczodrzynski.edziennik.ui.modules.grades.GradeView
android:id="@+id/gradeName"
@ -89,10 +88,7 @@
android:id="@+id/gradeCategory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:maxWidth="200dp"
android:maxLines="1"

View File

@ -41,8 +41,7 @@
android:id="@+id/normalTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="8dp"
android:text="@string/grades_stats_normal"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -51,8 +50,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
android:paddingHorizontal="8dp">
<LinearLayout
android:id="@+id/normalSemester1Layout"
@ -183,9 +181,8 @@
android:id="@+id/pointTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/grades_stats_point"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -194,8 +191,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
android:paddingHorizontal="8dp">
<LinearLayout
android:id="@+id/pointSemester1Layout"

View File

@ -64,8 +64,7 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:checked="true"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingVertical="16dp"
android:text="@string/login_allow_registration" />
<LinearLayout

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/mal_baseline_half">
<com.mikepenz.materialdrawer.view.BezelImageView
android:id="@+id/mal_item_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="@dimen/mal_baseline"
android:adjustViewBounds="false"
android:contentDescription="@null"
android:cropToPadding="false"
android:scaleType="centerCrop"
tools:background="@tools:sample/backgrounds/scenic" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="@dimen/mal_baseline"
android:layout_marginEnd="@dimen/mal_baseline"
android:layout_marginRight="@dimen/mal_baseline"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/mal_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:textColor="?android:textColorPrimary"
android:textSize="24sp"
tools:text="Władca Androida" />
<TextView
android:id="@+id/mal_item_desc"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?android:textColorSecondary"
android:textSize="14sp"
tools:text="kubasz" />
</LinearLayout>
</LinearLayout>

View File

@ -48,8 +48,7 @@
android:gravity="center"
android:lines="1"
android:minWidth="20dp"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:paddingHorizontal="1dp"
android:singleLine="true"
android:textSize="@dimen/material_drawer_item_badge_text"
app:layout_constraintBottom_toBottomOf="@id/material_drawer_account_header_current"
@ -82,8 +81,7 @@
android:gravity="center"
android:lines="1"
android:minWidth="20dp"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:paddingHorizontal="1dp"
android:singleLine="true"
android:textSize="@dimen/material_drawer_item_badge_small_text"
app:layout_constraintBottom_toBottomOf="@id/material_drawer_account_header_small_first"
@ -116,8 +114,7 @@
android:gravity="center"
android:lines="1"
android:minWidth="20dp"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:paddingHorizontal="1dp"
android:singleLine="true"
android:textSize="@dimen/material_drawer_item_badge_small_text"
app:layout_constraintBottom_toBottomOf="@id/material_drawer_account_header_small_second"
@ -150,8 +147,7 @@
android:gravity="center"
android:lines="1"
android:minWidth="20dp"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:paddingHorizontal="1dp"
android:singleLine="true"
android:textSize="@dimen/material_drawer_item_badge_small_text"
app:layout_constraintBottom_toBottomOf="@id/material_drawer_account_header_small_third"

View File

@ -7,10 +7,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="@dimen/material_drawer_vertical_padding"
android:paddingLeft="@dimen/material_drawer_vertical_padding"
android:paddingEnd="@dimen/material_drawer_vertical_padding"
android:paddingRight="@dimen/material_drawer_vertical_padding">
android:paddingHorizontal="@dimen/material_drawer_vertical_padding">
<com.mikepenz.materialdrawer.view.BezelImageView
android:id="@+id/material_drawer_profileIcon"
@ -98,8 +95,7 @@
android:gravity="center"
android:lines="1"
android:minWidth="20dp"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:paddingHorizontal="1dp"
android:singleLine="true"
android:textSize="@dimen/material_drawer_item_primary_text"
tools:text="99" />

View File

@ -10,10 +10,7 @@
android:gravity="start|center_vertical"
android:minHeight="@dimen/md_simpleitem_height"
android:orientation="horizontal"
android:paddingEnd="@dimen/md_dialog_frame_margin"
android:paddingLeft="@dimen/md_dialog_frame_margin"
android:paddingRight="@dimen/md_dialog_frame_margin"
android:paddingStart="@dimen/md_dialog_frame_margin">
android:paddingHorizontal="@dimen/md_dialog_frame_margin">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"

View File

@ -117,9 +117,8 @@
android:background="?selectableItemBackground"
android:ellipsize="end"
android:maxLines="2"
android:paddingLeft="8dp"
android:paddingHorizontal="8dp"
android:paddingTop="12dp"
android:paddingRight="8dp"
android:textAppearance="@style/NavView.TextView.Subtitle"
tools:text="Allegro - wysyłamy duużo wiadomości!!! Masz nowe oferty! Możesz kupić nowego laptopa! Ale super! Ehh, to jest nadawca a nie temat więc nwm czemu to tutaj wpisałem" />
@ -139,11 +138,9 @@
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginVertical="8dp"
android:minHeight="250dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
android:textIsSelectable="true"
tools:text="To jest treść wiadomości.\n\nZazwyczaj ma wiele linijek.\n\nTak" />
@ -155,9 +152,8 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:text="Odbiorcy wiadomości:"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -165,17 +161,15 @@
android:id="@+id/recipients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
tools:text=" - Jan Kowalski, przeczytano: nie\n - Adam Dodatkowy, przeczytano: 20 marca, 17:35" />
<TextView
android:id="@+id/attachmentsTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:text="Załączniki:"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -183,15 +177,13 @@
android:id="@+id/attachmentsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"/>
android:layout_marginHorizontal="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="8dp"
android:orientation="horizontal"
android:visibility="visible"
tools:visibility="visible">
@ -200,16 +192,13 @@
android:id="@+id/replyButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp">
android:paddingHorizontal="4dp"
android:paddingVertical="8dp">
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/replyIcon"
@ -233,16 +222,13 @@
android:id="@+id/forwardButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp">
android:paddingHorizontal="4dp"
android:paddingVertical="8dp">
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/forwardIcon"
@ -267,16 +253,13 @@
android:id="@+id/deleteButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp"
android:paddingHorizontal="4dp"
android:paddingVertical="8dp"
android:visibility="visible">
<com.mikepenz.iconics.view.IconicsImageView
@ -300,16 +283,13 @@
android:id="@+id/downloadButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp"
android:paddingHorizontal="4dp"
android:paddingVertical="8dp"
android:visibility="visible">
<com.mikepenz.iconics.view.IconicsImageView

View File

@ -87,8 +87,7 @@
android:id="@+id/fontStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="4dp">
<com.google.android.material.button.MaterialButton
@ -125,8 +124,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="8dp"
android:text="Breakpoints!"
android:visibility="gone"/>

View File

@ -59,12 +59,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:layout_margin="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@ -132,9 +127,8 @@
android:background="?selectableItemBackground"
android:ellipsize="end"
android:maxLines="3"
android:paddingLeft="8dp"
android:paddingHorizontal="8dp"
android:paddingTop="12dp"
android:paddingRight="8dp"
android:textAppearance="@style/NavView.TextView.Subtitle"
app:layout_constraintEnd_toStartOf="@+id/messageDate"
app:layout_constraintStart_toEndOf="@+id/messageProfileBackground"
@ -159,12 +153,10 @@
android:id="@+id/messageBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginVertical="8dp"
android:autoLink="all"
android:minHeight="250dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
android:textIsSelectable="true"
tools:text="To jest treść wiadomości.\n\nZazwyczaj ma wiele linijek.\n\nTak" />
@ -176,9 +168,8 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:text="Odbiorcy wiadomości:"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -186,17 +177,15 @@
android:id="@+id/messageRecipients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
tools:text=" - Jan Kowalski, przeczytano: nie\n - Adam Dodatkowy, przeczytano: 20 marca, 17:35" />
<TextView
android:id="@+id/messageAttachmentsTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:text="Załączniki:"
android:textAppearance="@style/NavView.TextView.Subtitle" />
@ -205,8 +194,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingHorizontal="16dp"
android:paddingBottom="8dp">
<FrameLayout
@ -235,8 +223,7 @@
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical|end"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
android:layout_marginHorizontal="8dp" />
</FrameLayout>
@ -264,8 +251,7 @@
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical|end"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp" />
android:layout_marginHorizontal="8dp" />
</FrameLayout>
@ -274,8 +260,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal"
android:visibility="gone"
@ -285,16 +270,13 @@
android:id="@+id/messageReplyButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp">
android:paddingHorizontal="4dp"
android:paddingVertical="8dp">
<TextView
android:layout_width="wrap_content"
@ -316,16 +298,13 @@
android:id="@+id/messageForwardButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp">
android:paddingHorizontal="4dp"
android:paddingVertical="8dp">
<TextView
android:layout_width="wrap_content"
@ -348,16 +327,13 @@
android:id="@+id/messageRemoveButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/bg_rounded_ripple"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingTop="8dp"
android:paddingRight="4dp"
android:paddingBottom="8dp">
android:paddingHorizontal="4dp"
android:paddingVertical="8dp">
<TextView
android:layout_width="wrap_content"

View File

@ -50,8 +50,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:adjustViewBounds="true"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:paddingVertical="2dp"
android:scaleType="fitCenter"
app:iiv_icon="cmd-attachment"
app:iiv_color="?android:textColorSecondary"
@ -64,10 +63,7 @@
android:id="@+id/messageSubject"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:singleLine="true"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
@ -97,10 +93,7 @@
android:id="@+id/messageBody"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="12dp"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Helper"

View File

@ -39,10 +39,7 @@
android:id="@+id/messageSubject"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:singleLine="true"
android:textStyle="normal"
android:textAppearance="@style/NavView.TextView.Helper"
@ -73,10 +70,7 @@
android:id="@+id/messageBody"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginBottom="12dp"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Helper"
@ -105,8 +99,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:adjustViewBounds="true"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:paddingVertical="2dp"
android:scaleType="fitCenter"
app:iiv_color="?android:textColorSecondary"
app:iiv_icon="cmd-attachment"

View File

@ -8,9 +8,8 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
@ -30,8 +29,7 @@
android:id="@+id/checkbox"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginHorizontal="12dp"
android:background="@drawable/recaptcha_checkbox_border"
android:foreground="?selectableItemBackgroundBorderless">
@ -67,8 +65,7 @@
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginLeft="13dp"
android:layout_marginRight="13dp"
android:layout_marginHorizontal="13dp"
app:srcCompat="@drawable/recaptcha"/>
<TextView
android:layout_width="wrap_content"

View File

@ -4,8 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp">
android:paddingVertical="8dp">
<TextView
android:id="@+id/attendanceType"
@ -44,10 +43,7 @@
android:id="@+id/attendanceSubject"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:singleLine="true"
@ -60,10 +56,7 @@
android:id="@+id/attendanceTeacher"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/NavView.TextView.Helper"
@ -77,10 +70,9 @@
android:id="@+id/attendanceTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginVertical="0dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="0dp"
android:textAppearance="@style/NavView.TextView.Helper"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -80,11 +80,8 @@
android:id="@+id/eventListItemTeacherName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/NavView.TextView.Helper"
@ -134,11 +131,8 @@
android:id="@+id/eventListItemSharedBy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ellipsize="end"
android:maxLines="1"
tools:text="{cmd-share-variant} przez Jan Kowalski"

View File

@ -10,10 +10,8 @@
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="4dp"
android:background="?selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">

View File

@ -38,8 +38,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:windowBackground"
android:paddingTop="8dp"
android:paddingBottom="8dp">
android:paddingVertical="8dp">
<TextView
android:id="@+id/gradesListName"
@ -62,8 +61,7 @@
android:id="@+id/gradesListCategory"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="marquee"
android:focusableInTouchMode="false"
android:marqueeRepeatLimit="marquee_forever"

View File

@ -14,9 +14,8 @@
app:cardElevation="4dp"
app:cardCornerRadius="5dp"
android:background="?selectableItemBackground"
android:paddingLeft="10dp"
android:paddingHorizontal="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="3dp">
<LinearLayout
@ -44,8 +43,7 @@
android:id="@+id/homeworkItemSharedBy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"

View File

@ -15,8 +15,7 @@
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" />
android:layout_marginVertical="8dp" />
<LinearLayout
android:layout_width="0dp"

View File

@ -14,9 +14,8 @@
app:cardElevation="4dp"
app:cardCornerRadius="5dp"
android:background="?selectableItemBackground"
android:paddingLeft="10dp"
android:paddingHorizontal="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="3dp">
<LinearLayout
@ -34,8 +33,7 @@
android:id="@+id/noticesItemType"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginVertical="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />

View File

@ -10,10 +10,8 @@
android:id="@+id/timetableItemCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="4dp"
android:background="?selectableItemBackground"
app:cardCornerRadius="5dp"
app:cardElevation="4dp">
@ -89,10 +87,7 @@
android:id="@+id/timetableItemSubjectChange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/timetableItemTeamName"
app:layout_constraintStart_toEndOf="@+id/timetableItemEndTime"
@ -103,10 +98,7 @@
android:id="@+id/timetableItemSubjectName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginHorizontal="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textSize="18sp"
@ -149,8 +141,7 @@
<com.mikepenz.iconics.view.IconicsTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginVertical="8dp"
android:text="{cmd-clock}"
app:layout_constraintBottom_toTopOf="@+id/timetableItemEndTime"
app:layout_constraintEnd_toEndOf="@+id/timetableItemEndTime"

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widgetNotificationsRoot"
android:layout_width="match_parent"
@ -11,8 +10,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_marginVertical="2dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="8dp">

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widgetNotificationsRoot"
android:layout_width="match_parent"
@ -11,8 +10,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_marginVertical="2dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="8dp">

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widgetNotificationsRoot"
android:layout_width="match_parent"
@ -11,8 +10,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_marginVertical="2dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="8dp">

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widgetNotificationsRoot"
android:layout_width="match_parent"
@ -11,8 +10,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_marginVertical="2dp"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="8dp">

Some files were not shown because too many files have changed in this diff Show More