diff --git a/app/build.gradle b/app/build.gradle index 447e4cc1..eadc01cc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,7 +130,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" @@ -140,6 +140,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" @@ -148,7 +149,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" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6e8fc491..15919246 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -143,8 +143,7 @@ - - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index d7855364..af15b543 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -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 @@ -295,19 +291,6 @@ fun colorFromCssName(name: String): Int { fun List.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" @@ -1235,3 +1218,41 @@ operator fun Iterable>.get(key: K): V? { } fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) } + +fun MutableList.after(what: E, insert: E) { + val index = indexOf(what) + if (index != -1) + add(index + 1, insert) +} + +fun MutableList.before(what: E, insert: E) { + val index = indexOf(what) + if (index != -1) + add(index, insert) +} + +fun MutableList.after(what: E, insert: Collection) { + val index = indexOf(what) + if (index != -1) + addAll(index + 1, insert) +} + +fun MutableList.before(what: E, insert: Collection) { + 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 +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index cdb9778f..951a2cd7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -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 } @@ -395,7 +391,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 { @@ -475,28 +477,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() @@ -578,7 +559,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) }) } } @@ -586,7 +567,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) @@ -834,7 +815,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope { handleIntent(intent?.extras) } } - private fun handleIntent(extras: Bundle?) { + fun handleIntent(extras: Bundle?) { d(TAG, "handleIntent() {") extras?.keySet()?.forEach { key -> @@ -992,13 +973,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) } /* _ _ _ _ _ @@ -1224,6 +1199,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() + } + } + /* _____ _ _ | __ \ (_) | | | | |_ __ __ ___ _____ _ __ _| |_ ___ _ __ ___ ___ @@ -1299,26 +1287,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() private var targetHomeId: Int = -1 override fun onBackPressed() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivityRequestHandler.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivityRequestHandler.kt new file mode 100644 index 00000000..a39a5872 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivityRequestHandler.kt @@ -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() + private val listeners = mutableMapOf 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 { + 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) + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncConfigDialog.kt new file mode 100644 index 00000000..13dcb6f9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncConfigDialog.kt @@ -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? { + 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(android.R.id.title) + val editText = dialog.findViewById(android.R.id.text1) + val textLayout = dialog.findViewById(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() + } + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt index 7240934b..97e34009 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt @@ -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 - saveEvent() - } + if (share && profile.registration != Profile.REGISTRATION_ENABLED) { + RegistrationConfigDialog(activity, profile, onChangeListener = { enabled -> + if (enabled) + saveEvent() + }).showEventShareDialog() return } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt index 4877413f..5b9579ed 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradesConfigDialog.kt similarity index 99% rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradesConfigDialog.kt index e74f3577..20325447 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradesConfigDialog.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileConfigDialog.kt new file mode 100644 index 00000000..e9204090 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileConfigDialog.kt @@ -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() + } + } + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileRemoveDialog.kt similarity index 95% rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileRemoveDialog.kt index 1fa16230..7071f00d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ProfileRemoveDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/profile/ProfileRemoveDialog.kt @@ -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() }} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AppLanguageDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AppLanguageDialog.kt new file mode 100644 index 00000000..6db33e70 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AppLanguageDialog.kt @@ -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() + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/MiniMenuConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/MiniMenuConfigDialog.kt new file mode 100644 index 00000000..61e95a8b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/MiniMenuConfigDialog.kt @@ -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() + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ThemeChooserDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ThemeChooserDialog.kt new file mode 100644 index 00000000..ca9b2f11 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/ThemeChooserDialog.kt @@ -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() + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/QuietHoursConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/QuietHoursConfigDialog.kt new file mode 100644 index 00000000..5c933e20 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/QuietHoursConfigDialog.kt @@ -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") + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt new file mode 100644 index 00000000..fff65b43 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt @@ -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) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationEnableDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationEnableDialog.kt deleted file mode 100644 index 7fc0caf3..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationEnableDialog.kt +++ /dev/null @@ -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) - }} -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/SyncIntervalDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/SyncIntervalDialog.kt new file mode 100644 index 00000000..6f4f027b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/SyncIntervalDialog.kt @@ -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() + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt index 29a0b2b3..b1b84f9d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/debug/LabPageFragment.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt index eb7fdcb1..63521e97 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesListFragment.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt index de1b071f..532ade84 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/MaterialAboutProfileItem.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/MaterialAboutProfileItem.kt new file mode 100644 index 00000000..dfff3246 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/MaterialAboutProfileItem.kt @@ -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) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsCard.kt new file mode 100644 index 00000000..36ce3777 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsCard.kt @@ -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 + protected open fun getItemsMore(): List = listOf() +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsFragment.kt new file mode 100644 index 00000000..cf7fa625 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsFragment.kt @@ -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, + ) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsLicenseActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsLicenseActivity.kt index 2e5aaf6f..e0c6be5e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsLicenseActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsLicenseActivity.kt @@ -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 + private val icon + get() = IconicsDrawable(this).apply { + icon = CommunityMaterial.Icon.cmd_book_outline + colorInt = foregroundColor + sizeDp = 18 + } + override fun onCreate(savedInstanceState: Bundle?) { - val app = application as App - setTheme(Themes.appTheme) - foregroundColor = Themes.getPrimaryTextColor(this) + 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) } - private fun createLicenseCard( - context: Context, - libraryTitle: CharSequence, - copyrightYear: CharSequence, - copyrightName: CharSequence, - license: OpenSourceLicense, - libraryUrl: String): MaterialAboutCard { - val licenseItem = MaterialAboutActionItem.Builder() - .icon(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() + 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 { + 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" + ), - return MaterialAboutList( - createLicenseCard(this, - "OkHttp", - "", - "square", - OpenSourceLicense.APACHE_2, - "https://github.com/square/okhttp/"), - createLicenseCard(this, - "MHttp", - "2018", - "Mot.", - OpenSourceLicense.APACHE_2, - "https://github.com/motcwang/MHttp/"), - createLicenseCard(this, - "AgendaCalendarView", - "2015", - "Thibault Guégan", - OpenSourceLicense.APACHE_2, - "https://github.com/Tibolte/AgendaCalendarView/"), - createLicenseCard(this, - "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, - "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, - "CircularProgressIndicator", - "2018", - "Anton Kozyriatskyi", - OpenSourceLicense.APACHE_2, - "https://github.com/antonKozyriatskyi/CircularProgressIndicator/") + license( + "Android Jetpack", + "", + "The Android Open Source Project", + OpenSourceLicense.APACHE_2, + "https://github.com/androidx/androidx" + ), + license( + "Material Components for Android", + "2014-2020", + "Google, Inc.", + OpenSourceLicense.APACHE_2, + "https://github.com/material-components/material-components-android" + ), - /*createLicenseCard(this, - "NoNonsense-FilePicker", - "", - "Jonas Kalderstam (spacecowboy)", - OpenSourceLicense.GNU_GPL_3, - "https://github.com/spacecowboy/NoNonsense-FilePicker/")*/ + 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/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" + ), + + license( + "Android Swipe Layout", + "2014", + "代码家", + OpenSourceLicense.MIT, + "https://github.com/daimajia/AndroidSwipeLayout" + ), + + license( + "CircularProgressIndicator", + "2018", + "Anton Kozyriatskyi", + OpenSourceLicense.APACHE_2, + "https://github.com/antonKozyriatskyi/CircularProgressIndicator" + ), + + license( + "ChatMessageView", + "2019", + "Tsubasa Nakayama", + OpenSourceLicense.APACHE_2, + "https://github.com/bassaer/ChatMessageView" + ), + + 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) } - } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java deleted file mode 100644 index d009cdf1..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ /dev/null @@ -1,1286 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.settings; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.media.MediaPlayer; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.provider.Settings; -import android.widget.Toast; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.danielstone.materialaboutlibrary.ConvenienceBuilder; -import com.danielstone.materialaboutlibrary.MaterialAboutFragment; -import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem; -import com.danielstone.materialaboutlibrary.items.MaterialAboutActionSwitchItem; -import com.danielstone.materialaboutlibrary.items.MaterialAboutItem; -import com.danielstone.materialaboutlibrary.items.MaterialAboutItemOnClickAction; -import com.danielstone.materialaboutlibrary.items.MaterialAboutSwitchItem; -import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem; -import com.danielstone.materialaboutlibrary.model.MaterialAboutCard; -import com.danielstone.materialaboutlibrary.model.MaterialAboutList; -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.IconicsConvertersKt; -import com.mikepenz.iconics.utils.IconicsDrawableExtensionsKt; -import com.theartofdev.edmodo.cropper.CropImage; -import com.theartofdev.edmodo.cropper.CropImageView; -import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import eu.szkolny.font.SzkolnyFont; -import kotlin.Unit; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.BuildConfig; -import pl.szczodrzynski.edziennik.ExtensionsKt; -import pl.szczodrzynski.edziennik.MainActivity; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi; -import pl.szczodrzynski.edziennik.data.db.entity.LoginStore; -import pl.szczodrzynski.edziennik.network.NetworkUtils; -import pl.szczodrzynski.edziennik.sync.SyncWorker; -import pl.szczodrzynski.edziennik.sync.UpdateWorker; -import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; -import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog; -import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog; -import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog; -import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog; -import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity; -import pl.szczodrzynski.edziennik.utils.Themes; -import pl.szczodrzynski.edziennik.utils.Utils; -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.Time; - -import static android.app.Activity.RESULT_OK; -import static pl.szczodrzynski.edziennik.ExtensionsKt.initDefaultLocale; -import static pl.szczodrzynski.edziennik.data.db.entity.Profile.REGISTRATION_DISABLED; -import static pl.szczodrzynski.edziennik.data.db.entity.Profile.REGISTRATION_ENABLED; -import static pl.szczodrzynski.edziennik.utils.Utils.d; -import static pl.szczodrzynski.edziennik.utils.Utils.getRealPathFromURI; -import static pl.szczodrzynski.edziennik.utils.Utils.getResizedBitmap; -import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_AVG_2_AVG; -import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_AVG_2_SEM; -import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_SEM_2_AVG; -import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_1_SEM_2_SEM; -import static pl.szczodrzynski.edziennik.utils.managers.GradesManager.YEAR_ALL_GRADES; - -public class SettingsNewFragment extends MaterialAboutFragment { - - private static final String TAG = "Settings"; - private App app = null; - private MainActivity activity; - - private static final int CARD_PROFILE = 0; - private static final int CARD_THEME = 1; - private static final int CARD_SYNC = 2; - private static final int CARD_REGISTER = 3; - private static final int CARD_ABOUT = 4; - private int iconColor = Color.WHITE; - private int primaryTextOnPrimaryBg = -1; - private int secondaryTextOnPrimaryBg = -1; - private int iconSizeDp = 20; - - private IconicsDrawable icon(IIcon icon, int sizeDp, int color) { - return new IconicsDrawable(activity).apply((drawable) -> { - drawable.setIcon(icon); - IconicsConvertersKt.setSizeDp(drawable, sizeDp); - IconicsDrawableExtensionsKt.setColorInt(drawable, color); - return Unit.INSTANCE; - }); - } - - private MaterialAboutCard getCardWithItems(CharSequence title, ArrayList items, boolean primaryColor) { - MaterialAboutCard card = new MaterialAboutCard.Builder().title(title).cardColor(0xff1976D2).build(); - card.getItems().addAll(items); - return card; - } - private MaterialAboutCard getCardWithItems(CharSequence title, ArrayList items) { - MaterialAboutCard card = new MaterialAboutCard.Builder().title(title).cardColor(Utils.getAttr(activity, R.attr.mal_card_background)).build(); - card.getItems().addAll(items); - return card; - } - private void addCardItems(int cardIndex, ArrayList itemsNew) { - ArrayList items = getList().getCards().get(cardIndex).getItems(); - items.remove(items.size() - 1); - items.addAll(itemsNew); - refreshMaterialAboutList(); - } - private void addCardItem(int cardIndex, int itemIndex, MaterialAboutItem item) { - ArrayList items = getList().getCards().get(cardIndex).getItems(); - items.add(itemIndex, item); - refreshMaterialAboutList(); - } - private void removeCardItem(int cardIndex, int itemIndex) { - ArrayList items = getList().getCards().get(cardIndex).getItems(); - items.remove(itemIndex); - refreshMaterialAboutList(); - } - private MaterialAboutActionItem getMoreItem(MaterialAboutItemOnClickAction onClickAction) { - return new MaterialAboutActionItem( - getString(R.string.settings_more_text), - null, - icon(CommunityMaterial.Icon.cmd_chevron_down, 14, iconColor), - onClickAction - ); - } - - - /* _____ __ _ _ - | __ \ / _(_) | - | |__) | __ ___ | |_ _| | ___ - | ___/ '__/ _ \| _| | |/ _ \ - | | | | | (_) | | | | | __/ - |_| |_| \___/|_| |_|_|\__*/ - private Drawable getProfileDrawable() { - return app.getProfile().getImageDrawable(activity); - - /*Bitmap profileImage = null; - if (app.getProfile().getImage() != null && !app.getProfile().getImage().equals("")) { - profileImage = BitmapFactory.decodeFile(app.getProfile().getImage()); - RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(getResources(), profileImage); - roundDrawable.setCircular(true); - return roundDrawable; - } - return getDrawableFromRes(getContext(), R.drawable.profile);*/ - - /*if (profileImage == null) { - profileImage = BitmapFactory.decodeResource(getResources(), R.drawable.profile); - } - profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight())); - RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(getResources(), profileImage); - roundDrawable.setCircular(true); - return roundDrawable;*/ - } - private MaterialAboutTitleItem profileCardTitleItem; - private ArrayList getProfileCard(boolean expandedOnly) { - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - - profileCardTitleItem = new MaterialAboutTitleItem( - app.getProfile().getName(), - app.getProfile().getSubname(), - getProfileDrawable() - ); - profileCardTitleItem.setOnClickAction(() -> { - new MaterialDialog.Builder(activity) - .title(R.string.settings_profile_change_title) - .items( - getString(R.string.settings_profile_change_name), - getString(R.string.settings_profile_change_image), - app.getProfile().getImage() == null ? null : getString(R.string.settings_profile_remove_image) - ) - .itemsCallback((dialog, itemView, position, text) -> { - switch (position) { - case 0: - new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_profile_change_name_dialog_title)) - .input(getString(R.string.settings_profile_change_name_dialog_text), app.getProfile().getName(), (dialog1, input) -> { - app.getProfile().setName(input.toString()); - profileCardTitleItem.setText(input); - profileCardTitleItem.setIcon(getProfileDrawable()); - refreshMaterialAboutList(); - app.profileSave(); - }) - .positiveText(R.string.ok) - .negativeText(R.string.cancel) - .show(); - break; - case 1: - startActivityForResult(CropImage.getPickImageChooserIntent(activity), 21); - break; - case 2: - app.getProfile().setImage(null); - profileCardTitleItem.setIcon(getProfileDrawable()); - refreshMaterialAboutList(); - app.profileSave(); - break; - } - }) - .negativeText(R.string.cancel) - .show(); - }); - items.add(profileCardTitleItem); - - /*items.add( - new MaterialAboutActionItem( - getString(R.string.settings_profile_change_password_text), - getString(R.string.settings_profile_change_password_subtext), - icon(CommunityMaterial.Icon2.cmd_key_variant, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - - }) - );*/ - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_add_student_text), - getString(R.string.settings_add_student_subtext), - icon(CommunityMaterial.Icon.cmd_account_plus_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - startActivity(new Intent(activity, LoginActivity.class)); - }) - ); - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_profile_notifications_text), - getString(R.string.settings_profile_notifications_subtext), - icon(CommunityMaterial.Icon2.cmd_filter_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - new NotificationFilterDialog(activity, null, null); - }) - ); - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_profile_remove_text), - getString(R.string.settings_profile_remove_subtext), - icon(SzkolnyFont.Icon.szf_delete_empty_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - new ProfileRemoveDialog(activity, app.getProfile().getId(), app.getProfile().getName(), false); - }) - ); - - items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true)))); - } - else { - - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_profile_sync_text) - .subText(R.string.settings_profile_sync_subtext) - .icon(icon(CommunityMaterial.Icon.cmd_account_convert, iconSizeDp, iconColor)) - .setChecked(app.getProfile().getSyncEnabled()) - .setOnCheckedChanged(((item, isChecked) -> { - app.getProfile().setSyncEnabled(isChecked); - app.profileSave(); - return true; - })) - .build() - ); - - } - return items; - } - - /* _______ _ - |__ __| | - | | | |__ ___ _ __ ___ ___ - | | | '_ \ / _ \ '_ ` _ \ / _ \ - | | | | | | __/ | | | | | __/ - |_| |_| |_|\___|_| |_| |_|\__*/ - private ArrayList getThemeCard(boolean expandedOnly) { - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - - Date today = Date.getToday(); - if (today.month == 12 || today.month == 1) { - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_theme_snowfall_text) - .subText(R.string.settings_theme_snowfall_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_snowflake, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getUi().getSnowfall()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().getUi().setSnowfall(isChecked); - activity.recreate(); - return true; - }) - .build() - ); - } - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_theme_theme_text), - Themes.INSTANCE.getThemeName(activity), - icon(CommunityMaterial.Icon3.cmd_palette_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - new MaterialDialog.Builder(activity) - .title(R.string.settings_theme_theme_text) - .items(Themes.INSTANCE.getThemeNames(activity)) - .itemsCallbackSingleChoice(app.getConfig().getUi().getTheme(), (dialog, itemView, which, text) -> { - if (app.getConfig().getUi().getTheme() == which) - return true; - app.getConfig().getUi().setTheme(which); - Themes.INSTANCE.setThemeInt(app.getConfig().getUi().getTheme()); - activity.recreate(); - return true; - }) - .show(); - }) - ); - - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_theme_mini_drawer_text) - .subText(R.string.settings_theme_mini_drawer_subtext) - .icon(icon(CommunityMaterial.Icon.cmd_dots_vertical, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getUi().getMiniMenuVisible()) - .setOnCheckedChanged((item, isChecked) -> { - // 0,1 1 - // 0,0 0 - // 1,1 0 - // 1,0 1 - app.getConfig().getUi().setMiniMenuVisible(isChecked); - activity.getNavView().drawer.setMiniDrawerVisiblePortrait(isChecked); - return true; - }) - .build() - ); - - items.add(getMoreItem(() -> addCardItems(CARD_THEME, getThemeCard(true)))); - } - else { - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_theme_mini_drawer_buttons_text), - null, - icon(CommunityMaterial.Icon2.cmd_format_list_checks, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - List buttonIds = new ArrayList<>(); - buttonIds.add(MainActivity.DRAWER_ITEM_HOME); - buttonIds.add(MainActivity.DRAWER_ITEM_TIMETABLE); - buttonIds.add(MainActivity.DRAWER_ITEM_AGENDA); - buttonIds.add(MainActivity.DRAWER_ITEM_GRADES); - buttonIds.add(MainActivity.DRAWER_ITEM_MESSAGES); - buttonIds.add(MainActivity.DRAWER_ITEM_HOMEWORK); - buttonIds.add(MainActivity.DRAWER_ITEM_BEHAVIOUR); - buttonIds.add(MainActivity.DRAWER_ITEM_ATTENDANCE); - buttonIds.add(MainActivity.DRAWER_ITEM_ANNOUNCEMENTS); - buttonIds.add(MainActivity.DRAWER_ITEM_NOTIFICATIONS); - buttonIds.add(MainActivity.DRAWER_ITEM_SETTINGS); - //buttonIds.add(MainActivity.DRAWER_ITEM_DEBUG); - List buttonCaptions = new ArrayList<>(); - buttonCaptions.add(getString(R.string.menu_home_page)); - buttonCaptions.add(getString(R.string.menu_timetable)); - buttonCaptions.add(getString(R.string.menu_agenda)); - buttonCaptions.add(getString(R.string.menu_grades)); - buttonCaptions.add(getString(R.string.menu_messages)); - buttonCaptions.add(getString(R.string.menu_homework)); - buttonCaptions.add(getString(R.string.menu_notices)); - buttonCaptions.add(getString(R.string.menu_attendance)); - buttonCaptions.add(getString(R.string.menu_announcements)); - buttonCaptions.add(getString(R.string.menu_notifications)); - buttonCaptions.add(getString(R.string.menu_settings)); - //buttonCaptions.add(getString(R.string.title_debugging)); - List selectedIds = new ArrayList<>(); - for (int id: app.getConfig().getUi().getMiniMenuButtons()) { - selectedIds.add(buttonIds.indexOf(id)); - } - new MaterialDialog.Builder(activity) - .title(R.string.settings_theme_mini_drawer_buttons_dialog_title) - .content(getString(R.string.settings_theme_mini_drawer_buttons_dialog_text)) - .items(buttonCaptions) - .itemsCallbackMultiChoice(selectedIds.toArray(new Integer[0]), (dialog, which, text) -> { - List list = new ArrayList<>(); - for (int index: which) { - if (index == -1) - continue; - // wtf - - int id = buttonIds.get(index); - list.add(id); - } - app.getConfig().getUi().setMiniMenuButtons(list); - activity.setDrawerItems(); - activity.getDrawer().updateBadges(); - return true; - }) - .positiveText(R.string.ok) - .show(); - }) - ); - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_theme_drawer_header_text), - null, - icon(CommunityMaterial.Icon2.cmd_image_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - if (app.getConfig().getUi().getHeaderBackground() != null) { - new MaterialDialog.Builder(activity) - .title(R.string.what_do_you_want_to_do) - .items(getString(R.string.settings_theme_drawer_header_dialog_set), getString(R.string.settings_theme_drawer_header_dialog_restore)) - .itemsCallback((dialog, itemView, position, text) -> { - if (position == 0) { - startActivityForResult(CropImage.getPickImageChooserIntent(activity), 22); - } else { - MainActivity ac = (MainActivity) getActivity(); - app.getConfig().getUi().setHeaderBackground(null); - if (ac != null) { - ac.getDrawer().setAccountHeaderBackground(null); - ac.getDrawer().open(); - } - } - }) - .show(); - } - else { - startActivityForResult(CropImage.getPickImageChooserIntent(activity), 22); - } - }) - ); - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_theme_app_background_text), - getString(R.string.settings_theme_app_background_subtext), - icon(CommunityMaterial.Icon2.cmd_image_filter_hdr, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - if (app.getConfig().getUi().getAppBackground() != null) { - new MaterialDialog.Builder(activity) - .title(R.string.what_do_you_want_to_do) - .items(getString(R.string.settings_theme_app_background_dialog_set), getString(R.string.settings_theme_app_background_dialog_restore)) - .itemsCallback((dialog, itemView, position, text) -> { - if (position == 0) { - startActivityForResult(CropImage.getPickImageChooserIntent(activity), 23); - } else { - app.getConfig().getUi().setAppBackground(null); - activity.recreate(); - } - }) - .show(); - } - else { - startActivityForResult(CropImage.getPickImageChooserIntent(activity), 23); - } - }) - ); - - items.add( - new MaterialAboutSwitchItem.Builder( ) - .text(R.string.settings_theme_open_drawer_on_back_pressed_text) - .icon(icon(CommunityMaterial.Icon3.cmd_menu_open, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getUi().getOpenDrawerOnBackPressed()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().getUi().setOpenDrawerOnBackPressed(isChecked); - return true; - }) - .build() - ); - } - return items; - } - - /* _____ - / ____| - | (___ _ _ _ __ ___ - \___ \| | | | '_ \ / __| - ____) | |_| | | | | (__ - |_____/ \__, |_| |_|\___| - __/ | - |__*/ - private String getSyncCardIntervalSubText() { - - if (app.getConfig().getSync().getInterval() < 60 * 60) - return getString( - R.string.settings_sync_sync_interval_subtext_format, - ExtensionsKt.plural(activity, R.plurals.time_till_minutes, app.getConfig().getSync().getInterval() / 60) - ); - return getString( - R.string.settings_sync_sync_interval_subtext_format, - ExtensionsKt.plural(activity, R.plurals.time_till_hours, app.getConfig().getSync().getInterval() / 60 / 60) + - (app.getConfig().getSync().getInterval() / 60 % 60 == 0 ? - "" : - " " + ExtensionsKt.plural(activity, R.plurals.time_till_minutes, app.getConfig().getSync().getInterval() / 60 % 60) - ) - ); - } - private String getSyncCardQuietHoursSubText() { - if (app.getConfig().getSync().getQuietHoursStart() == null || app.getConfig().getSync().getQuietHoursEnd() == null) - return ""; - return getString( - app.getConfig().getSync().getQuietHoursStart().getValue() >= app.getConfig().getSync().getQuietHoursEnd().getValue() ? R.string.settings_sync_quiet_hours_subtext_next_day_format : R.string.settings_sync_quiet_hours_subtext_format, - app.getConfig().getSync().getQuietHoursStart().getStringHM(), - app.getConfig().getSync().getQuietHoursEnd().getStringHM() - ); - } - private MaterialAboutItem getSyncCardWifiItem() { - return new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_sync_wifi_text) - .subText(R.string.settings_sync_wifi_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_wifi_strength_2, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getSync().getOnlyWifi()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().getSync().setOnlyWifi(isChecked); - SyncWorker.Companion.rescheduleNext(app); - return true; - }) - .build(); - } - private MaterialAboutActionSwitchItem syncCardIntervalItem; - private MaterialAboutActionSwitchItem syncCardQuietHoursItem; - private ArrayList getSyncCard(boolean expandedOnly) { - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - - syncCardIntervalItem = new MaterialAboutActionSwitchItem.Builder() - .text(R.string.settings_sync_sync_interval_text) - .subText(R.string.settings_sync_sync_interval_subtext_disabled) - .icon(icon(CommunityMaterial.Icon.cmd_download_outline, iconSizeDp, iconColor)) - .build(); - syncCardIntervalItem.setSubTextChecked(getSyncCardIntervalSubText()); - syncCardIntervalItem.setChecked(app.getConfig().getSync().getEnabled()); - syncCardIntervalItem.setOnClickAction(() -> { - List intervalNames = new ArrayList<>(); - if (App.Companion.getDebugMode() && false) { - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_seconds, 30)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_minutes, 2)); - } - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_minutes, 30)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_minutes, 45)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_hours, 1)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_hours, 1)+" "+ ExtensionsKt.plural(activity, R.plurals.time_till_minutes, 30)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_hours, 2)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_hours, 3)); - intervalNames.add(ExtensionsKt.plural(activity, R.plurals.time_till_hours, 4)); - List intervals = new ArrayList<>(); - if (App.Companion.getDebugMode() && false) { - intervals.add(30); - intervals.add(2 * 60); - } - intervals.add(30 * 60); - intervals.add(45 * 60); - intervals.add(60 * 60); - intervals.add(90 * 60); - intervals.add(120 * 60); - intervals.add(180 * 60); - intervals.add(240 * 60); - new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_sync_sync_interval_dialog_title)) - .content(getString(R.string.settings_sync_sync_interval_dialog_text)) - .items(intervalNames) - .itemsCallbackSingleChoice(intervals.indexOf(app.getConfig().getSync().getInterval()), (dialog, itemView, which, text) -> { - app.getConfig().getSync().setInterval(intervals.get(which)); - syncCardIntervalItem.setSubTextChecked(getSyncCardIntervalSubText()); - syncCardIntervalItem.setChecked(true); - if (!app.getConfig().getSync().getEnabled()) { - addCardItem(CARD_SYNC, 1, getSyncCardWifiItem()); - } - else { - refreshMaterialAboutList(); - } - app.getConfig().getSync().setEnabled(true); - // app.appConfig modifications have to surround syncCardIntervalItem and those ifs - SyncWorker.Companion.rescheduleNext(app); - return true; - }) - .show(); - }); - syncCardIntervalItem.setOnCheckedChangedAction((item, isChecked) -> { - if (isChecked && !app.getConfig().getSync().getEnabled()) { - addCardItem(CARD_SYNC, 1, getSyncCardWifiItem()); - } - else if (!isChecked) { - removeCardItem(CARD_SYNC, 1); - } - app.getConfig().getSync().setEnabled(isChecked); - SyncWorker.Companion.rescheduleNext(app); - return true; - }); - items.add(syncCardIntervalItem); - - if (app.getConfig().getSync().getEnabled()) { - items.add(getSyncCardWifiItem()); - } - - /* items.add( - new MaterialAboutSwitchItem( - "Cisza na lekcjach", - "Nie odtwarzaj dźwięku powiadomień podczas lekcji", - icon(CommunityMaterial.Icon2.cmd_volume_off, iconSizeDp, iconColor) - ) - .setChecked(app.appConfig.quietDuringLessons) - .setOnChangeAction((isChecked) -> { - app.appConfig.quietDuringLessons = isChecked; - app.saveConfig("quietDuringLessons"); - return true; - }) - );*/ - - - syncCardQuietHoursItem = new MaterialAboutActionSwitchItem.Builder() - .text(R.string.settings_sync_quiet_hours_text) - .subText(R.string.settings_sync_quiet_hours_subtext_disabled) - .icon(icon(CommunityMaterial.Icon.cmd_bell_sleep_outline, iconSizeDp, iconColor)) - .build(); - syncCardQuietHoursItem.setChecked(app.getConfig().getSync().getQuietHoursEnabled()); - syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText()); - syncCardQuietHoursItem.setOnClickAction(() -> { - new MaterialDialog.Builder(activity) - .title(R.string.settings_sync_quiet_hours_dialog_title) - .items( - getString(R.string.settings_sync_quiet_hours_set_beginning), - getString(R.string.settings_sync_quiet_hours_set_end) - ) - .itemsCallback((dialog, itemView, position, text) -> { - if (position == 0) { - // set beginning - Time time = app.getConfig().getSync().getQuietHoursStart(); - if (time == null) - time = new Time(22, 30, 0); - TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> { - app.getConfig().getSync().setQuietHoursEnabled(true); - app.getConfig().getSync().setQuietHoursStart(new Time(hourOfDay, minute, second)); - syncCardQuietHoursItem.setChecked(true); - syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText()); - refreshMaterialAboutList(); - }, time.hour, time.minute, 0, true).show(activity.getSupportFragmentManager(), "TimePickerDialog"); - } - else { - // set end - Time time = app.getConfig().getSync().getQuietHoursEnd(); - if (time == null) - time = new Time(5, 30, 0); - TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> { - app.getConfig().getSync().setQuietHoursEnabled(true); - app.getConfig().getSync().setQuietHoursEnd(new Time(hourOfDay, minute, second)); - syncCardQuietHoursItem.setChecked(true); - syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText()); - refreshMaterialAboutList(); - }, time.hour, time.minute, 0, true).show(activity.getSupportFragmentManager(), "TimePickerDialog"); - } - }) - .show(); - }); - syncCardQuietHoursItem.setOnCheckedChangedAction((item, isChecked) -> { - app.getConfig().getSync().setQuietHoursEnabled(isChecked); - if (isChecked && app.getConfig().getSync().getQuietHoursStart() == null) { - app.getConfig().getSync().setQuietHoursStart(new Time(22, 30, 0)); - app.getConfig().getSync().setQuietHoursEnd(new Time(5, 30, 0)); - syncCardQuietHoursItem.setSubTextChecked(getSyncCardQuietHoursSubText()); - refreshMaterialAboutList(); - } - return true; - }); - items.add(syncCardQuietHoursItem); - - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_sync_web_push_text), - getString(R.string.settings_sync_web_push_subtext), - icon(CommunityMaterial.Icon2.cmd_laptop, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - activity.loadTarget(MainActivity.TARGET_WEB_PUSH, null); - }) - ); - - items.add(getMoreItem(() -> addCardItems(CARD_SYNC, getSyncCard(true)))); - } - else { - // TODO: 2019-04-27 add notification sound options: szkolny.eu, system default, custom - /*items.add( - new MaterialAboutActionItem( - "Dźwięk powiadomień", - "Szkolny.eu", - icon(CommunityMaterial.Icon2.cmd_volume_high, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - - }) - );*/ - - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_sync_updates_text) - .icon(icon(CommunityMaterial.Icon.cmd_cellphone_arrow_down, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getSync().getNotifyAboutUpdates()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().getSync().setNotifyAboutUpdates(isChecked); - UpdateWorker.Companion.rescheduleNext(app); - return true; - }) - .build() - ); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - items.add( - new MaterialAboutActionItem( - getString(R.string.settings_sync_notifications_settings_text), - getString(R.string.settings_sync_notifications_settings_subtext), - icon(CommunityMaterial.Icon.cmd_cog_outline, iconSizeDp, iconColor) - ) - .setOnClickAction(() -> { - String channel = app.getNotificationChannelsManager().getData().getKey(); - Intent intent = new Intent(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel); - intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.getPackageName()); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (channel != null) { - intent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel); - } else { - intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); - } - intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.getPackageName()); - } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); - intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.getPackageName()); - } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ - intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); - intent.putExtra("app_package", activity.getPackageName()); - intent.putExtra("app_uid", activity.getApplicationInfo().uid); - } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { - intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setData(Uri.parse("package:" + activity.getPackageName())); - } - activity.startActivity(intent); - }) - ); - } - } - return items; - } - - /* _____ _ _ - | __ \ (_) | | - | |__) |___ __ _ _ ___| |_ ___ _ __ - | _ // _ \/ _` | / __| __/ _ \ '__| - | | \ \ __/ (_| | \__ \ || __/ | - |_| \_\___|\__, |_|___/\__\___|_| - __/ | - |__*/ - private String getRegisterCardAverageModeSubText() { - switch (app.getConfig().forProfile().getGrades().getYearAverageMode()) { - default: - case YEAR_1_AVG_2_AVG: - return getString(R.string.settings_register_avg_mode_0_short); - case YEAR_1_SEM_2_AVG: - return getString(R.string.settings_register_avg_mode_1_short); - case YEAR_1_AVG_2_SEM: - return getString(R.string.settings_register_avg_mode_2_short); - case YEAR_1_SEM_2_SEM: - return getString(R.string.settings_register_avg_mode_3_short); - case YEAR_ALL_GRADES: - return getString(R.string.settings_register_avg_mode_4_short); - } - } - private String getRegisterCardBellSyncSubText() { - if (app.getConfig().getTimetable().getBellSyncDiff() == null) - return getString(R.string.settings_register_bell_sync_subtext_disabled); - return getString( - R.string.settings_register_bell_sync_subtext_format, - (app.getConfig().getTimetable().getBellSyncMultiplier() == -1 ? "-" : "+") + app.getConfig().getTimetable().getBellSyncDiff().getStringHMS() - ); - } - private MaterialAboutItem getRegisterCardSharedEventsItem() { - return new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_register_shared_events_text) - .subText(R.string.settings_register_shared_events_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_share_outline, iconSizeDp, iconColor)) - .setChecked(app.getProfile().getEnableSharedEvents()) - .setOnCheckedChanged((item, isChecked) -> { - app.getProfile().setEnableSharedEvents(isChecked); - app.profileSave(); - if (isChecked) new MaterialDialog.Builder(activity) - .title(R.string.event_sharing) - .content(getString(R.string.settings_register_shared_events_dialog_enabled_text)) - .positiveText(R.string.ok) - .show(); - else new MaterialDialog.Builder(activity) - .title(R.string.event_sharing) - .content(getString(R.string.settings_register_shared_events_dialog_disabled_text)) - .positiveText(R.string.ok) - .show(); - return true; - }) - .build(); - } - - private MaterialAboutSwitchItem registerCardAllowRegistrationItem; - private MaterialAboutActionItem registerCardBellSyncItem; - private ArrayList getRegisterCard(boolean expandedOnly) { - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - items.add(new MaterialAboutActionItem( - getString(R.string.menu_grades_config), - null, - icon(CommunityMaterial.Icon3.cmd_numeric_5_box_outline, iconSizeDp, iconColor) - ).setOnClickAction(() -> new GradesConfigDialog(activity, false, null, null))); - - items.add(new MaterialAboutActionItem( - getString(R.string.menu_attendance_config), - null, - icon(CommunityMaterial.Icon.cmd_calendar_remove_outline, iconSizeDp, iconColor) - ).setOnClickAction(() -> new AttendanceConfigDialog(activity, false, null, null))); - - registerCardAllowRegistrationItem = new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_register_allow_registration_text) - .subText(R.string.settings_register_allow_registration_subtext) - .icon(icon(CommunityMaterial.Icon.cmd_account_circle_outline, iconSizeDp, iconColor)) - .build(); - registerCardAllowRegistrationItem.setChecked(app.getProfile().getRegistration() == REGISTRATION_ENABLED); - registerCardAllowRegistrationItem.setOnCheckedChangedAction((item, isChecked) -> { - if (isChecked) new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_register_allow_registration_dialog_enabled_title)) - .content(getString(R.string.settings_register_allow_registration_dialog_enabled_text)) - .positiveText(R.string.i_agree) - .negativeText(R.string.not_now) - .onPositive(((dialog, which) -> { - registerCardAllowRegistrationItem.setChecked(true); - app.getProfile().setRegistration(REGISTRATION_ENABLED); - app.profileSave(); - addCardItem(CARD_REGISTER, 2, getRegisterCardSharedEventsItem()); - refreshMaterialAboutList(); - })) - .show(); - else new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_register_allow_registration_dialog_disabled_title)) - .content(getString(R.string.settings_register_allow_registration_dialog_disabled_text)) - .positiveText(R.string.ok) - .negativeText(R.string.cancel) - .onPositive(((dialog, which) -> { - registerCardAllowRegistrationItem.setChecked(false); - app.getProfile().setRegistration(REGISTRATION_DISABLED); - app.profileSave(); - removeCardItem(CARD_REGISTER, 2); - refreshMaterialAboutList(); - MaterialDialog progressDialog = new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_register_allow_registration_dialog_disabling_title)) - .content(getString(R.string.settings_register_allow_registration_dialog_disabling_text)) - .positiveText(R.string.ok) - .negativeText(R.string.abort) - .show(); - AsyncTask.execute(() -> { - new SzkolnyApi(app).runCatching(szkolnyApi -> null, szkolnyApi -> null); - activity.runOnUiThread(() -> { - progressDialog.dismiss(); - Toast.makeText(activity, getString(R.string.settings_register_allow_registration_dialog_disabling_finished), Toast.LENGTH_SHORT).show(); - }); - }); - })) - .show(); - return false; - }); - items.add(registerCardAllowRegistrationItem); - - if (app.getProfile().getRegistration() == REGISTRATION_ENABLED) { - items.add(getRegisterCardSharedEventsItem()); - } - - items.add(getMoreItem(() -> addCardItems(CARD_REGISTER, getRegisterCard(true)))); - } - else { - registerCardBellSyncItem = new MaterialAboutActionItem( - getString(R.string.settings_register_bell_sync_text), - getRegisterCardBellSyncSubText(), - icon(SzkolnyFont.Icon.szf_alarm_bell_outline, iconSizeDp, iconColor) - ); - registerCardBellSyncItem.setOnClickAction(() -> { - new MaterialDialog.Builder(activity) - .title(R.string.bell_sync_title) - .content(R.string.bell_sync_adjust_content) - .positiveText(R.string.ok) - .negativeText(R.string.cancel) - .neutralText(R.string.reset) - .inputRangeRes(8, 8, R.color.md_red_500) - .input("±H:MM:SS", - (app.getConfig().getTimetable().getBellSyncDiff() != null - ? (app.getConfig().getTimetable().getBellSyncMultiplier() == -1 ? "-" : "+") + app.getConfig().getTimetable().getBellSyncDiff().getStringHMS() - : ""), (dialog, input) -> { - if (input == null) - return; - if (input.length() < 8) { - Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show(); - return; - } - if (input.charAt(2) != ':' || input.charAt(5) != ':') { - Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show(); - return; - } - if (input.charAt(0) == '+') { - app.getConfig().getTimetable().setBellSyncMultiplier(1); - } - else if (input.charAt(0) == '-') { - app.getConfig().getTimetable().setBellSyncMultiplier(-1); - } - else { - Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show(); - return; - } - - int hour; - int minute; - int second; - try { - hour = Integer.parseInt(input.charAt(1) + ""); - minute = Integer.parseInt(input.charAt(3) + "" + input.charAt(4)); - second = Integer.parseInt(input.charAt(6) + "" + input.charAt(7)); - } - catch (Exception e) { - e.printStackTrace(); - Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show(); - return; - } - - app.getConfig().getTimetable().setBellSyncDiff(new Time(hour, minute, second)); - registerCardBellSyncItem.setSubText(getRegisterCardBellSyncSubText()); - refreshMaterialAboutList(); - }) - .onNeutral(((dialog, which) -> { - app.getConfig().getTimetable().setBellSyncDiff(null); - app.getConfig().getTimetable().setBellSyncMultiplier(0); - registerCardBellSyncItem.setSubText(getRegisterCardBellSyncSubText()); - refreshMaterialAboutList(); - })) - .show(); - }); - items.add(registerCardBellSyncItem); - - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_register_count_in_seconds_text) - .subText(R.string.settings_register_count_in_seconds_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_timer, iconSizeDp, iconColor)) - .setChecked(app.getConfig().getTimetable().getCountInSeconds()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().getTimetable().setCountInSeconds(isChecked); - return true; - }) - .build() - ); - - if (app.getProfile().getLoginStoreType() == LoginStore.LOGIN_TYPE_LIBRUS) { - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_register_show_teacher_absences_text) - .icon(icon(CommunityMaterial.Icon.cmd_account_arrow_right_outline, iconSizeDp, iconColor)) - .setChecked(app.getProfile().getStudentData("showTeacherAbsences", true)) - .setOnCheckedChanged((item, isChecked) -> { - app.getProfile().putStudentData("showTeacherAbsences", isChecked); - app.profileSave(); - return true; - }) - .build() - ); - } - - if (App.Companion.getDevMode()) { - items.add( - new MaterialAboutSwitchItem.Builder() - .text(R.string.settings_register_hide_sticks_from_old) - .icon(icon(CommunityMaterial.Icon3.cmd_numeric_1_box_outline, iconSizeDp, iconColor)) - .setChecked(app.getConfig().forProfile().getGrades().getHideSticksFromOld()) - .setOnCheckedChanged((item, isChecked) -> { - app.getConfig().forProfile().getGrades().setHideSticksFromOld(isChecked); - return true; - }) - .build() - ); - } - - } - return items; - } - - /* _ _ - /\ | | | | - / \ | |__ ___ _ _| |_ - / /\ \ | '_ \ / _ \| | | | __| - / ____ \| |_) | (_) | |_| | |_ - /_/ \_\_.__/ \___/ \__,_|\_*/ - private MaterialAboutActionItem pref_about_version; - private ArrayList getAboutCard(boolean expandedOnly) { - primaryTextOnPrimaryBg = 0xffffffff;//getColorFromAttr(activity, R.attr.colorOnPrimary); - secondaryTextOnPrimaryBg = 0xd0ffffff;//activity.getResources().getColor(R.color.secondaryTextLight); - - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - items.add(new MaterialAboutTitleItem.Builder() - .text(R.string.app_name) - .desc(R.string.settings_about_title_subtext) - .icon(R.mipmap.ic_splash) - .build()); - - pref_about_version = new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_version_text) - .subText(BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE) - .icon(icon(CommunityMaterial.Icon2.cmd_information_outline, iconSizeDp, primaryTextOnPrimaryBg)) - .build(); - final int[] clickCounter = {0}; - pref_about_version.setOnClickAction(() -> { - if (6 - clickCounter[0] != 0) { - Toast.makeText(activity, ("\ud83d\ude02"), Toast.LENGTH_SHORT).show(); - } - refreshMaterialAboutList(); - clickCounter[0] = clickCounter[0] + 1; - if (clickCounter[0] > 6) { - final MediaPlayer mp = MediaPlayer.create(activity, R.raw.ogarnij_sie); - mp.start(); - clickCounter[0] = 0; - } - }); - items.add(pref_about_version); - - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_privacy_policy_text) - .icon(icon(CommunityMaterial.Icon3.cmd_shield_outline, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://szkolny.eu/privacy-policy"))) - .build()); - - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_discord_text) - .subText(R.string.settings_about_discord_subtext) - .icon(icon(SzkolnyFont.Icon.szf_discord_outline, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://szkolny.eu/discord"))) - .build()); - - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_language_text) - .subText(R.string.settings_about_language_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_translate, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(() -> { - new MaterialDialog.Builder(activity) - .title(getString(R.string.settings_about_language_dialog_title)) - .content(getString(R.string.settings_about_language_dialog_text)) - .items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english), getString(R.string.language_german)) - .itemsCallbackSingleChoice(app.getConfig().getUi().getLanguage() == null ? 0 : app.getConfig().getUi().getLanguage().equals("pl") ? 1 : app.getConfig().getUi().getLanguage().equals("en") ? 2 : 3, (dialog, itemView, which, text) -> { - switch (which) { - case 0: - app.getConfig().getUi().setLanguage(null); - initDefaultLocale(); - break; - case 1: - app.getConfig().getUi().setLanguage("pl"); - break; - case 2: - app.getConfig().getUi().setLanguage("en"); - break; - case 3: - app.getConfig().getUi().setLanguage("de"); - break; - } - activity.recreate(MainActivity.DRAWER_ITEM_SETTINGS); - return true; - }) - .show(); - }) - .build()); - - items.add(getMoreItem(() -> addCardItems(CARD_ABOUT, getAboutCard(true)))); - } - else { - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_update_text) - .subText(R.string.settings_about_update_subtext) - .icon(icon(CommunityMaterial.Icon3.cmd_update, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(() -> { - //open browser or intent here - NetworkUtils net = new NetworkUtils(app); - if (!net.isOnline()) { - new MaterialDialog.Builder(activity) - .title(R.string.you_are_offline_title) - .content(R.string.you_are_offline_text) - .positiveText(R.string.ok) - .show(); - } else { - AsyncTask.execute(() -> { - new UpdateWorker.JavaWrapper(app); - }); - } - }) - .build()); - - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_changelog_text) - .icon(icon(CommunityMaterial.Icon3.cmd_radar, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(() -> new ChangelogDialog(activity, null, null)) - .build()); - - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_licenses_text) - .icon(icon(CommunityMaterial.Icon.cmd_code_braces, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(() -> { - Intent intent = new Intent(activity, SettingsLicenseActivity.class); - startActivity(intent); - }) - .build()); - - /*items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_intro_text) - .icon(icon(CommunityMaterial.Icon2.cmd_projector_screen, iconSizeDp, iconColor)) - .setOnClickAction(() -> { - if (tryingToDevMode[0]) { - if (getParentFragment() instanceof SettingsGeneralFragment) { - ((SettingsGeneralFragment) getParentFragment()).showDevSettings(); - } - tryingToDevMode[0] = false; - return; - } - IntroConnectionFragment.connectionOk = true; - IntroConnectionFragment.httpsOk = (app.requestScheme.equals("https")); - Intent intent = new Intent(activity, MainIntroActivity.class); - IntroSplashFragment.skipAnimation = false; - startActivity(intent); - }) - .build());*/ - - if (App.Companion.getDevMode()) { - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_crash_text) - .subText(R.string.settings_about_crash_subtext) - .icon(icon(CommunityMaterial.Icon.cmd_bug_outline, iconSizeDp, primaryTextOnPrimaryBg)) - .setOnClickAction(() -> { - throw new RuntimeException("MANUAL CRASH"); - }) - .build()); - } - } - return items; - } - - @Override - protected MaterialAboutList getMaterialAboutList(Context activityContext) { - if (getActivity() == null || getContext() == null || !isAdded()) - return null; - this.activity = (MainActivity) activityContext; - this.app = (App) activity.getApplication(); - iconColor = Themes.INSTANCE.getPrimaryTextColor(activityContext); - if (app.getProfile() == null) - return new MaterialAboutList.Builder().build(); - - MaterialAboutList materialAboutList = new MaterialAboutList(); - materialAboutList.addCard(getCardWithItems(null, getProfileCard(false))); - materialAboutList.addCard(getCardWithItems(getString(R.string.settings_theme_title_text), getThemeCard(false))); - materialAboutList.addCard(getCardWithItems(getString(R.string.settings_sync_title_text), getSyncCard(false))); - materialAboutList.addCard(getCardWithItems(getString(R.string.settings_about_register_title_text), getRegisterCard(false))); - //if (configurableEndpoints != null) - // materialAboutList.addCard(getCardWithItems(getString(R.string.settings_sync_customize_title_text), getSyncCustomizeCard(false))); - materialAboutList.addCard(getCardWithItems(null, getAboutCard(false), true)); - - return materialAboutList; - } - - /* _____ _ ____ _ _ - / ____| | | | _ \ | | | | - | | _ _ ___| |_ ___ _ __ ___ | |_) | __ _ ___| | ____ _ _ __ ___ _ _ _ __ __| |___ - | | | | | / __| __/ _ \| '_ ` _ \ | _ < / _` |/ __| |/ / _` | '__/ _ \| | | | '_ \ / _` / __| - | |___| |_| \__ \ || (_) | | | | | | | |_) | (_| | (__| < (_| | | | (_) | |_| | | | | (_| \__ \ - \_____\__,_|___/\__\___/|_| |_| |_| |____/ \__,_|\___|_|\_\__, |_| \___/ \__,_|_| |_|\__,_|___/ - __/ | - |__*/ - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (data == null) { - Toast.makeText(app, "Wystąpił błąd. Spróbuj ponownie", Toast.LENGTH_SHORT).show(); - return; - } - if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) { - CropImage.ActivityResult result = CropImage.getActivityResult(data); - - Uri uri = result.getUri(); - - File photoFile = new File(uri.getPath()); - File destFile = new File(getContext().getFilesDir(),"profile"+ app.getProfile().getId() +".jpg"); - if (destFile.exists()) { - destFile.delete(); - destFile = new File(getContext().getFilesDir(),"profile"+ app.getProfile().getId() +".jpg"); - } - - d(TAG, "Source file: "+photoFile.getAbsolutePath()); - d(TAG, "Dest file: "+destFile.getAbsolutePath()); - - if (result.getCropRect().width() > 512 || true) { - Bitmap bigImage = BitmapFactory.decodeFile(uri.getPath()); - Bitmap smallImage = getResizedBitmap(bigImage, 512, 512); - try (FileOutputStream out = new FileOutputStream(destFile)) { - smallImage.compress(Bitmap.CompressFormat.JPEG, 80, out); // bmp is your Bitmap instance - // PNG is a lossless format, the compression factor (100) is ignored - app.getProfile().setImage(destFile.getAbsolutePath()); - app.profileSave(); - profileCardTitleItem.setIcon(getProfileDrawable()); - refreshMaterialAboutList(); - if (photoFile.exists()) { - photoFile.delete(); - } - //((MainActivity)getActivity()).recreateWithTransition(); // TODO somehow update miniDrawer profile picture - } catch (IOException e) { - e.printStackTrace(); - } - } - else { - if (photoFile.renameTo(destFile)) { - // success - app.getProfile().setImage(destFile.getAbsolutePath()); - app.profileSave(); - profileCardTitleItem.setIcon(getProfileDrawable()); - refreshMaterialAboutList(); - //((MainActivity)getActivity()).recreateWithTransition(); // TODO somehow update miniDrawer profile picture - } - else { - // not this time - Toast.makeText(app, R.string.error_occured, Toast.LENGTH_LONG).show(); - } - } - } - else if (requestCode == 21 && resultCode == Activity.RESULT_OK) { - Uri uri = data.getData(); - if (uri != null) { - String path = getRealPathFromURI(activity, uri); - if (path.toLowerCase().endsWith(".gif")) { - app.getProfile().setImage(path); - app.profileSave(); - profileCardTitleItem.setIcon(getProfileDrawable()); - refreshMaterialAboutList(); - } - else { - CropImage.activity(data.getData()) - .setAspectRatio(1, 1) - //.setMaxCropResultSize(512, 512) - .setCropShape(CropImageView.CropShape.OVAL) - .setGuidelines(CropImageView.Guidelines.ON) - .start(activity, this); - } - } - - } - else if (requestCode == 22 && resultCode == Activity.RESULT_OK) { - Uri uri = data.getData(); - if (uri != null) { - app.getConfig().getUi().setHeaderBackground(getRealPathFromURI(getContext(), uri)); - if (activity != null) { - activity.getDrawer().setAccountHeaderBackground(app.getConfig().getUi().getHeaderBackground()); - activity.getDrawer().open(); - } - } - } - else if (requestCode == 23 && resultCode == Activity.RESULT_OK) { - Uri uri = data.getData(); - if (uri != null) { - app.getConfig().getUi().setAppBackground(getRealPathFromURI(getContext(), uri)); - if (activity != null) { - activity.recreate(); - } - } - } - - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsUtil.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsUtil.kt new file mode 100644 index 00000000..258f4a17 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsUtil.kt @@ -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, + itemsMore: List, + 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 + ): 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 + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsViewTypeManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsViewTypeManager.kt new file mode 100644 index 00000000..1fb14fcd --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsViewTypeManager.kt @@ -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) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsAboutCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsAboutCard.kt new file mode 100644 index 00000000..97e4a538 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsAboutCard.kt @@ -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() + override fun getItemsMore() = listOf() + + 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 + )) + ) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsProfileCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsProfileCard.kt new file mode 100644 index 00000000..f88a0242 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsProfileCard.kt @@ -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)) + } + ) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsRegisterCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsRegisterCard.kt new file mode 100644 index 00000000..14afd0cb --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsRegisterCard.kt @@ -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 + } + ) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsSyncCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsSyncCard.kt new file mode 100644 index 00000000..1d42b86b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsSyncCard.kt @@ -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 + ) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsThemeCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsThemeCard.kt new file mode 100644 index 00000000..17b108e5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/cards/SettingsThemeCard.kt @@ -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() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/QrScannerActivity.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/QrScannerActivity.java deleted file mode 100644 index b9028ac5..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/QrScannerActivity.java +++ /dev/null @@ -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(); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/WebPushFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/WebPushFragment.kt index 4c8c34cc..0996c518 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/WebPushFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/webpush/WebPushFragment.kt @@ -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) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/SnackbarHelper.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/SnackbarHelper.java deleted file mode 100644 index 3fe2648b..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/SnackbarHelper.java +++ /dev/null @@ -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)); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/Themes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/Themes.kt index e2189f71..5f62b794 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/Themes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/Themes.kt @@ -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 { - val list = mutableListOf() - for (theme in themeList) { - list += context.getString(theme.name) + @StringRes + fun getThemeNameRes() = theme.name + + fun getThemeNames(context: Context) = + themeList.map { + context.getString(it.name) } - return list - } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/PermissionManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/PermissionManager.kt index 4b80f44b..65430f60 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/PermissionManager.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/PermissionManager.kt @@ -31,49 +31,91 @@ 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 { - true + 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( - activity: AppCompatActivity, - @StringRes permissionMessage: Int, - onSuccess: suspend CoroutineScope.() -> Unit + 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) - } - .setNegativeButton(R.string.cancel, null) - .show() + .setTitle(R.string.permissions_required) + .setMessage(permissionMessage) + .setPositiveButton(R.string.ok) { _, _ -> + requestPermission( + activity, + permissionMessage, + isRequired, + permissionName, + onSuccess + ) + } + .setNegativeButton(R.string.cancel, null) + .show() } result.hasPermanentDenied() -> { MaterialAlertDialogBuilder(activity) - .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) - } - .setNegativeButton(R.string.cancel, null) - .show() + .setTitle(R.string.permissions_required) + .setMessage(R.string.permissions_denied) + .setPositiveButton(R.string.ok) { _, _ -> + openPermissionSettings(activity) + } + .setNegativeButton(R.string.cancel, null) + .show() } } } } + + 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 + ) } diff --git a/app/src/main/res/drawable/bg_rounded_ripple_4dp_pressed.xml b/app/src/main/res/drawable/bg_rounded_ripple_4dp_pressed.xml deleted file mode 100644 index aadf2255..00000000 --- a/app/src/main/res/drawable/bg_rounded_ripple_4dp_pressed.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_snackbar.xml b/app/src/main/res/drawable/bg_snackbar.xml deleted file mode 100644 index 91835112..00000000 --- a/app/src/main/res/drawable/bg_snackbar.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_edit_text.xml b/app/src/main/res/layout/dialog_edit_text.xml new file mode 100644 index 00000000..6937705e --- /dev/null +++ b/app/src/main/res/layout/dialog_edit_text.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_profile_config.xml b/app/src/main/res/layout/dialog_profile_config.xml new file mode 100644 index 00000000..ecba48da --- /dev/null +++ b/app/src/main/res/layout/dialog_profile_config.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/mal_material_about_profile_item.xml b/app/src/main/res/layout/mal_material_about_profile_item.xml new file mode 100644 index 00000000..4b94b21e --- /dev/null +++ b/app/src/main/res/layout/mal_material_about_profile_item.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6e37bf58..9949f2e2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -351,8 +351,6 @@ Hinzugefügt %1$s%3$s {cmd-share-variant} %1$s von %2$s%3$s {cmd-share-variant} %1$s von Ihnen%3$s - Um das Ereignis freigeben zu können, müssen Sie die Serverregistrierungsoption aktivieren. Auf diese Weise können Sie Ereignisse erstellen und empfangen, die in Ihrer Klasse geteilt werden.\n\nWenn Sie auf OK klicken, wird diese automatisch aktiviert.\n\nStellen Sie sicher, dass Sie die Bedingungen lesen und akzeptieren. - Ereignisse teilen Ereignis entfernen… Ereignis speichern… Ereignis teilen… @@ -830,9 +828,13 @@ etzt bewerten Zeigen Sie, dass Ihnen Szkolny.eu gefällt - bewerten Sie die App und machen Sie sie noch besser! Aktualisieren - Die Registrierung erfolgt automatisch, wenn diese Option aktiviert ist. Sie können Ereignisse erstellen und empfangen, die mit anderen Schülern in Ihrer Klasse geteilt werden. Dank dessen können Sie Elemente, die nicht vom Lehrer gespeichert wurden, zum Klassenbuch hinzufügen.\n\nStellen Sie sicher, dass Sie die Bestimmungen der Datenschutzrichtlinie lesen und die Bestimmungen akzeptieren. - Server-Registrierung - Gemeinsame Ereignisse herunterladen… + Benutzerregistrierung aufheben… + Einige Funktionen wie Ereignisfreigabe oder Benachrichtigungsweiterleitung können nicht mehr verwendet werden.\n\nDie Funktion kann je nach ausgewähltem Profil aktiviert / deaktiviert werden. + Gemeinsame Ereignisse herunterladen… + Die Registrierung erfolgt automatisch, wenn diese Option aktiviert ist. Sie können Ereignisse erstellen und empfangen, die mit anderen Schülern in Ihrer Klasse geteilt werden. Dank dessen können Sie Elemente, die nicht vom Lehrer gespeichert wurden, zum Klassenbuch hinzufügen.\n\nStellen Sie sicher, dass Sie die Bestimmungen der Datenschutzrichtlinie lesen und die Bestimmungen akzeptieren. + Um das Ereignis freigeben zu können, müssen Sie die Serverregistrierungsoption aktivieren. Auf diese Weise können Sie Ereignisse erstellen und empfangen, die in Ihrer Klasse geteilt werden.\n\nWenn Sie auf OK klicken, wird diese automatisch aktiviert.\n\nStellen Sie sicher, dass Sie die Bedingungen lesen und akzeptieren. + Ereignisse teilen + Szkolny.eu Server-Registrierung Löschen gelöschtet Melden @@ -847,13 +849,13 @@ Klicken Sie hier, um eine unerwartete Ausnahme auszulösen Treten Sie unserem Discord-Server bei! Discord Server - Hinweis. Diese Option funktioniert möglicherweise auf einigen Geräten und in einigen Teilen der App nicht. - App-Sprache ändern + Hinweis. Diese Option funktioniert möglicherweise auf einigen Geräten und in einigen Teilen der App nicht. + App-Sprache ändern Deutsch Sprache der App Open-Source-Lizenzen Datenschutzrichtlinie - E-Klassenbuch + E-Klassenbuch © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - Februar 2021 Klicken Sie hier, um nach Aktualisierungen zu suchen Aktualisierung @@ -944,7 +946,7 @@ Deaktiviert Daten alle %s herunterladen Automatische Synchronisierung - Synchronisation und Benachrichtigungen + Synchronisation und Benachrichtigungen Über App-Aktualisierungen benachrichtigen Benachrichtigungen auf Ihrem PC anzeigen Benachrichtigungsweiterleitung @@ -968,7 +970,7 @@ Rosa System Thema - Aussehen + Aussehen Teilen Teilen über… Daten Teilen… diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index ab80b93c..2f5cb1fe 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -351,8 +351,6 @@ Adde %1$s%3$s {cmd-share-variant} %1$s by %2$s%3$s {cmd-share-variant} %1$s by you%3$s - You need to turn on server registration, in order to share an event. This option lets you create and receive shared events in your class.\n\nIt will be turned on automatically by pressing OK.\n\nBefore turning it on, make sure you\'ve read the terms and accept them - Sharing events Removing event… Saving event… Sharing event… @@ -832,9 +830,13 @@ Rate now Show that you like Szkolny.eu - rate the app and make it even better! Refresh - Registration is automatic if this option is enabled. It allows you to create and receive events shared with other students in your class. Thanks to this, you can add items not saved by the teacher to the journal.\n\nMake sure you read the terms and accept them. - Server registration - Syncing shared events… + Unregistering the user… + You won\'t be able to use some functions anymore, like Event sharing and Notification forwarding.\n\nThis feature may be enabled/disabled depending on the active profile. + Syncing shared events… + Registration is automatic if this option is enabled. It allows you to create and receive events shared with other students in your class. Thanks to this, you can add items not saved by the teacher to the journal.\n\nContinuing means reading and accepting the Privacy policy. + You need to turn on server registration, in order to share an event. This option lets you create and receive shared events in your class.\n\nIt will be turned on automatically by pressing OK.\n\nContinuing means reading and accepting the Privacy policy. + Sharing events + Szkolny.eu server registration Remove Removed Report @@ -849,13 +851,13 @@ Click to throw an unexpected exception Join our Discord community! Discord server - Notice. This feature may not work on some devices or in some parts of the app. - Change app language - "English " + Notice. This feature may not work on some devices or in some parts of the app. + Change app language + English App language Open-source licenses Privacy policy - E-register + E-register © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - February 2021 Click to check for updates Update @@ -946,7 +948,7 @@ Disabled Download data every %s Automatic sync - Sync & notifications + Sync & notifications Notify about app updates Show notifications on your PC Notification forwarding @@ -970,7 +972,7 @@ Pink System Theme - Appearance + Appearance Share Share… Sharing events… diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ea45e49..a5f8621f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -386,8 +386,6 @@ Dodano %1$s%3$s {cmd-share-variant} %1$s przez %2$s%3$s {cmd-share-variant} %1$s przez Ciebie%3$s - Aby móc udostępnić wydarzenie, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie wydarzeń udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nUpewnij się, że zapoznałeś się z warunkami i akceptujesz jej postanowienia. - Udostępnianie wydarzeń Usuwam wydarzenie… Zapisuję wydarzenie… Udostępniam wydarzenie… @@ -895,9 +893,13 @@ Oceń teraz Pokaż, że podoba ci się Szkolny.eu - oceń aplikację i spraw, by była jeszcze lepsza! Odśwież - Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nUpewnij się, że zapoznałeś się z warunkami Polityki prywatności i akceptujesz jej postanowienia. - Rejestracja na serwerze - Pobieranie udostępnionych wydarzeń… + Trwa wyrejestrowywanie użytkownika… + Stracisz możliwość korzystania z niektórych funkcji, takich jak Udostępnianie wydarzeń czy Przekazywanie powiadomień.\n\nFunkcja może być włączona/wyłączona w zależności od wybranego profilu. + Pobieranie udostępnionych wydarzeń… + Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień Polityki prywatności. + Aby móc udostępnić wydarzenie, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie wydarzeń udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień Polityki prywatności. + Udostępnianie wydarzeń + Rejestracja aplikacji Szkolny.eu Usuń Usunięto Zgłoś @@ -912,13 +914,13 @@ Kliknij, aby wywołać nieoczekiwany wyjątek Dołącz do naszego serwera Discord! Serwer Discord - Uwaga. Ta opcja może nie działać na niektórych urządzeniach oraz w niektórych fragmentach aplikacji. - Zmień język aplikacji + Uwaga. Ta opcja może nie działać na niektórych urządzeniach oraz w niektórych fragmentach aplikacji. + Zmień język aplikacji Polski Język aplikacji Licencje open-source Polityka prywatności - E-dziennik + E-dziennik © Kuba Szczodrzyński && Kacper Ziubryniewicz\nwrzesień 2018 - luty 2021 Kliknij, aby sprawdzić aktualizacje Aktualizacja @@ -1011,7 +1013,7 @@ Wyłączona Pobieraj dane co %s Synchronizacja automatyczna - Synchronizacja i powiadomienia + Synchronizacja i powiadomienia Powiadamiaj o aktualizacjach aplikacji Pokazuj powiadomienia na swoim komputerze Przekazywanie powiadomień @@ -1035,7 +1037,7 @@ Różowy Systemowy Motyw - Wygląd + Wygląd Udostępnij Udostępnij przez… Udostępnianie danych… @@ -1387,4 +1389,14 @@ {cmd-android-studio} Wersja deweloperska \??? Szkoda, opinie innych pomagają mi rozwijać aplikację. + Nie znaleziono profilu ucznia. + Zobacz także + Wejdź na stronę aplikacji + Uzyskaj pomoc lub wesprzyj autorów + Kod źródłowy + Pomóż w rozwoju aplikacji na GitHubie + Nazwa profilu + Synchronizuj ten profil + Wyloguj się + Aby móc zeskanować kod QR musisz przyznać uprawnienia dostępu do kamery.\n\nKliknij OK, aby przyznać uprawnienia.