mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-31 13:48:20 +01:00
[API] Implement Librus Captcha. Refactor notification constants. Update empty account error as a dialog.
This commit is contained in:
parent
f5e1e9fdd9
commit
4ad826ebe8
@ -4,9 +4,6 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik
|
package pl.szczodrzynski.edziennik
|
||||||
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.ShortcutInfo
|
import android.content.pm.ShortcutInfo
|
||||||
@ -48,6 +45,8 @@ import pl.szczodrzynski.edziennik.sync.SyncWorker
|
|||||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||||
import pl.szczodrzynski.edziennik.utils.*
|
import pl.szczodrzynski.edziennik.utils.*
|
||||||
|
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
|
||||||
|
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -63,25 +62,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
var devMode = false
|
var devMode = false
|
||||||
}
|
}
|
||||||
|
|
||||||
val notifications by lazy { Notifications() }
|
val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
|
||||||
inner class Notifications {
|
val userActionManager by lazy { UserActionManager(this) }
|
||||||
val syncId = 1
|
|
||||||
val syncKey = "pl.szczodrzynski.edziennik.SYNC"
|
|
||||||
val syncChannelName: String by lazy { getString(R.string.notification_channel_get_data_name) }
|
|
||||||
val syncChannelDesc: String by lazy { getString(R.string.notification_channel_get_data_desc) }
|
|
||||||
val dataId = 50
|
|
||||||
val dataKey = "pl.szczodrzynski.edziennik.DATA"
|
|
||||||
val dataChannelName: String by lazy { getString(R.string.notification_channel_notifications_name) }
|
|
||||||
val dataChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_desc) }
|
|
||||||
val dataQuietId = 60
|
|
||||||
val dataQuietKey = "pl.szczodrzynski.edziennik.DATA_QUIET"
|
|
||||||
val dataQuietChannelName: String by lazy { getString(R.string.notification_channel_notifications_quiet_name) }
|
|
||||||
val dataQuietChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_quiet_desc) }
|
|
||||||
val updatesId = 100
|
|
||||||
val updatesKey = "pl.szczodrzynski.edziennik.UPDATES"
|
|
||||||
val updatesChannelName: String by lazy { getString(R.string.notification_channel_updates_name) }
|
|
||||||
val updatesChannelDesc: String by lazy { getString(R.string.notification_channel_updates_desc) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val db
|
val db
|
||||||
get() = App.db
|
get() = App.db
|
||||||
@ -99,7 +81,6 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
.setMinimumLoggingLevel(Log.VERBOSE)
|
.setMinimumLoggingLevel(Log.VERBOSE)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
|
|
||||||
val permissionChecker by lazy { PermissionChecker(this) }
|
val permissionChecker by lazy { PermissionChecker(this) }
|
||||||
val networkUtils by lazy { NetworkUtils(this) }
|
val networkUtils by lazy { NetworkUtils(this) }
|
||||||
val gson by lazy { Gson() }
|
val gson by lazy { Gson() }
|
||||||
@ -251,29 +232,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
)
|
)
|
||||||
} // shortcuts - end
|
} // shortcuts - end
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
notificationChannelsManager.registerAllChannels()
|
||||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
notificationManager.createNotificationChannel(
|
|
||||||
NotificationChannel(notifications.syncKey, notifications.syncChannelName, NotificationManager.IMPORTANCE_MIN).apply {
|
|
||||||
description = notifications.syncChannelDesc
|
|
||||||
})
|
|
||||||
notificationManager.createNotificationChannel(
|
|
||||||
NotificationChannel(notifications.dataKey, notifications.dataChannelName, NotificationManager.IMPORTANCE_HIGH).apply {
|
|
||||||
description = notifications.dataChannelDesc
|
|
||||||
enableLights(true)
|
|
||||||
lightColor = 0xff2196f3.toInt()
|
|
||||||
})
|
|
||||||
notificationManager.createNotificationChannel(
|
|
||||||
NotificationChannel(notifications.dataQuietKey, notifications.dataQuietChannelName, NotificationManager.IMPORTANCE_LOW).apply {
|
|
||||||
description = notifications.dataQuietChannelDesc
|
|
||||||
setSound(null, null)
|
|
||||||
enableVibration(false)
|
|
||||||
})
|
|
||||||
notificationManager.createNotificationChannel(
|
|
||||||
NotificationChannel(notifications.updatesKey, notifications.updatesChannelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
|
|
||||||
description = notifications.updatesChannelDesc
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (config.appInstalledTime == 0L)
|
if (config.appInstalledTime == 0L)
|
||||||
|
@ -659,6 +659,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onUserActionRequiredEvent(event: UserActionRequiredEvent) {
|
||||||
|
app.userActionManager.execute(this, event.profileId, event.type)
|
||||||
|
}
|
||||||
|
|
||||||
private fun fragmentToSyncName(currentFragment: Int): Int {
|
private fun fragmentToSyncName(currentFragment: Int): Int {
|
||||||
return when (currentFragment) {
|
return when (currentFragment) {
|
||||||
@ -713,11 +717,20 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
intentTargetId = TARGET_FEEDBACK
|
intentTargetId = TARGET_FEEDBACK
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
"userActionRequired" -> {
|
||||||
|
app.userActionManager.execute(
|
||||||
|
this,
|
||||||
|
extras.getInt("profileId"),
|
||||||
|
extras.getInt("type")
|
||||||
|
)
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
if (handled)
|
if (handled && !navLoading) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (extras?.containsKey("reloadProfileId") == true) {
|
if (extras?.containsKey("reloadProfileId") == true) {
|
||||||
val reloadProfileId = extras.getInt("reloadProfileId", -1)
|
val reloadProfileId = extras.getInt("reloadProfileId", -1)
|
||||||
|
@ -86,9 +86,16 @@ class ApiService : Service() {
|
|||||||
lastEventTime = System.currentTimeMillis()
|
lastEventTime = System.currentTimeMillis()
|
||||||
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
||||||
apiError.profileId = taskProfileId
|
apiError.profileId = taskProfileId
|
||||||
|
|
||||||
|
if (app.userActionManager.requiresUserAction(apiError)) {
|
||||||
|
app.userActionManager.sendToUser(apiError)
|
||||||
|
}
|
||||||
|
else {
|
||||||
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
|
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
|
||||||
errorList.add(apiError)
|
errorList.add(apiError)
|
||||||
apiError.throwable?.printStackTrace()
|
apiError.throwable?.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
if (apiError.isCritical) {
|
if (apiError.isCritical) {
|
||||||
taskRunning?.cancel()
|
taskRunning?.cancel()
|
||||||
notification.setCriticalError().post()
|
notification.setCriticalError().post()
|
||||||
@ -301,7 +308,7 @@ class ApiService : Service() {
|
|||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
d(TAG, "Foreground service onStartCommand")
|
d(TAG, "Foreground service onStartCommand")
|
||||||
startForeground(app.notifications.syncId, notification.notification)
|
startForeground(app.notificationChannelsManager.sync.id, notification.notification)
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ class EdziennikNotification(val app: App) {
|
|||||||
fun post() {
|
fun post() {
|
||||||
if (serviceClosed)
|
if (serviceClosed)
|
||||||
return
|
return
|
||||||
notificationManager.notify(app.notifications.syncId, notification)
|
notificationManager.notify(app.notificationChannelsManager.sync.id, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,9 @@ const val ERROR_FILE_DOWNLOAD = 113
|
|||||||
|
|
||||||
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
|
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
|
||||||
|
|
||||||
|
const val ERROR_CAPTCHA_NEEDED = 3000
|
||||||
|
const val ERROR_CAPTCHA_LIBRUS_PORTAL = 3001
|
||||||
|
|
||||||
const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120
|
const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120
|
||||||
const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121
|
const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121
|
||||||
const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124
|
const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124
|
||||||
|
@ -33,9 +33,8 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
val accounts = json.getJsonArray("accounts")
|
val accounts = json.getJsonArray("accounts")
|
||||||
|
|
||||||
if (accounts == null || accounts.size() < 1) {
|
if (accounts == null || accounts.size() < 1) {
|
||||||
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
|
EventBus.getDefault().post(FirstLoginFinishedEvent(listOf(), data.loginStore))
|
||||||
.withResponse(response)
|
onSuccess()
|
||||||
.withApiResponse(json))
|
|
||||||
return@portalGet
|
return@portalGet
|
||||||
}
|
}
|
||||||
val accountDataTime = json.getLong("lastModification")
|
val accountDataTime = json.getLong("lastModification")
|
||||||
|
@ -7,12 +7,10 @@ import im.wangchao.mhttp.Response
|
|||||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.getInt
|
|
||||||
import pl.szczodrzynski.edziennik.getString
|
|
||||||
import pl.szczodrzynski.edziennik.getUnixDate
|
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -38,11 +36,21 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else if (data.portalRefreshToken != null) {
|
else if (data.portalRefreshToken != null) {
|
||||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
if (data.fakeLogin) {
|
||||||
accessToken(null, data.portalRefreshToken)
|
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
||||||
|
}
|
||||||
|
accessToken(null, data.portalRefreshToken)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (data.fakeLogin) {
|
||||||
|
data.app.cookieJar.clearForDomain("librus.szkolny.eu")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
||||||
|
}
|
||||||
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -89,11 +97,20 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
private fun login(csrfToken: String) {
|
private fun login(csrfToken: String) {
|
||||||
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL}")
|
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL}")
|
||||||
|
|
||||||
|
val recaptchaCode = data.arguments?.getString("recaptchaCode") ?: data.loginStore.getLoginData("recaptchaCode", null)
|
||||||
|
val recaptchaTime = data.arguments?.getLong("recaptchaTime") ?: data.loginStore.getLoginData("recaptchaTime", 0L)
|
||||||
|
data.loginStore.removeLoginData("recaptchaCode")
|
||||||
|
data.loginStore.removeLoginData("recaptchaTime")
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL)
|
.url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL)
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.addParameter("email", data.portalEmail)
|
.addParameter("email", data.portalEmail)
|
||||||
.addParameter("password", data.portalPassword)
|
.addParameter("password", data.portalPassword)
|
||||||
|
.also {
|
||||||
|
if (recaptchaCode != null && System.currentTimeMillis() - recaptchaTime < 2*60*1000 /* 2 minutes */)
|
||||||
|
it.addParameter("g-recaptcha-response", recaptchaCode)
|
||||||
|
}
|
||||||
.addHeader("X-CSRF-TOKEN", csrfToken)
|
.addHeader("X-CSRF-TOKEN", csrfToken)
|
||||||
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
||||||
.post()
|
.post()
|
||||||
@ -117,6 +134,12 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
.withResponse(response))
|
.withResponse(response))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (json.getBoolean("captchaRequired") == true) {
|
||||||
|
data.error(ApiError(TAG, ERROR_CAPTCHA_LIBRUS_PORTAL)
|
||||||
|
.withResponse(response)
|
||||||
|
.withApiResponse(json))
|
||||||
|
return
|
||||||
|
}
|
||||||
if (json.get("errors") != null) {
|
if (json.get("errors") != null) {
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR)
|
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR)
|
||||||
.withResponse(response)
|
.withResponse(response)
|
||||||
|
@ -6,14 +6,12 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin
|
|||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.ERROR_NO_STUDENTS_IN_ACCOUNT
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||||
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_STUDENT_LIST
|
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_STUDENT_LIST
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
|
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
@ -35,9 +33,8 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
|
|||||||
val students = json.getJsonArray("Data")
|
val students = json.getJsonArray("Data")
|
||||||
|
|
||||||
if (students == null || students.isEmpty()) {
|
if (students == null || students.isEmpty()) {
|
||||||
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
|
EventBus.getDefault().post(FirstLoginFinishedEvent(listOf(), data.loginStore))
|
||||||
.withResponse(response)
|
onSuccess()
|
||||||
.withApiResponse(json))
|
|
||||||
return@apiGet
|
return@apiGet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.events
|
||||||
|
|
||||||
|
data class UserActionRequiredEvent(val profileId: Int, val type: Int) {
|
||||||
|
companion object {
|
||||||
|
const val LOGIN_DATA_MOBIDZIENNIK = 101
|
||||||
|
const val LOGIN_DATA_LIBRUS = 102
|
||||||
|
const val LOGIN_DATA_IDZIENNIK = 103
|
||||||
|
const val LOGIN_DATA_VULCAN = 104
|
||||||
|
const val LOGIN_DATA_EDUDZIENNIK = 105
|
||||||
|
const val CAPTCHA_LIBRUS = 202
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
MainActivity::class.java,
|
MainActivity::class.java,
|
||||||
"fragmentId" to MainActivity.DRAWER_ITEM_NOTIFICATIONS
|
"fragmentId" to MainActivity.DRAWER_ITEM_NOTIFICATIONS
|
||||||
)
|
)
|
||||||
val summaryIntent = PendingIntent.getActivity(app, app.notifications.dataId, intent, PendingIntent.FLAG_ONE_SHOT)
|
val summaryIntent = PendingIntent.getActivity(app, app.notificationChannelsManager.data.id, intent, PendingIntent.FLAG_ONE_SHOT)
|
||||||
|
|
||||||
// On Nougat or newer - show maximum 8 notifications
|
// On Nougat or newer - show maximum 8 notifications
|
||||||
// On Marshmallow or older - show maximum 4 notifications
|
// On Marshmallow or older - show maximum 4 notifications
|
||||||
@ -89,7 +89,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a summary to show *instead* of notifications
|
// Create a summary to show *instead* of notifications
|
||||||
val combined = NotificationCompat.Builder(app, app.notifications.dataKey)
|
val combined = NotificationCompat.Builder(app, app.notificationChannelsManager.data.key)
|
||||||
.setContentTitle(app.getString(R.string.app_name))
|
.setContentTitle(app.getString(R.string.app_name))
|
||||||
.setContentText(buildSummaryText(summaryCounts))
|
.setContentText(buildSummaryText(summaryCounts))
|
||||||
.setTicker(newNotificationsText)
|
.setTicker(newNotificationsText)
|
||||||
@ -112,7 +112,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
.setGroup(app.notifications.dataKey)
|
.setGroup(app.notificationChannelsManager.data.key)
|
||||||
.setContentIntent(summaryIntent)
|
.setContentIntent(summaryIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.build()
|
.build()
|
||||||
@ -122,7 +122,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
// Less than 8 notifications
|
// Less than 8 notifications
|
||||||
val notifications = nList.map {
|
val notifications = nList.map {
|
||||||
summaryCounts[it.type]++
|
summaryCounts[it.type]++
|
||||||
NotificationCompat.Builder(app, app.notifications.dataKey)
|
NotificationCompat.Builder(app, app.notificationChannelsManager.data.key)
|
||||||
.setContentTitle(it.profileName ?: app.getString(R.string.app_name))
|
.setContentTitle(it.profileName ?: app.getString(R.string.app_name))
|
||||||
.setContentText(it.text)
|
.setContentText(it.text)
|
||||||
.setSubText(if (it.type == TYPE_SERVER_MESSAGE) null else it.title)
|
.setSubText(if (it.type == TYPE_SERVER_MESSAGE) null else it.title)
|
||||||
@ -135,7 +135,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
.setGroup(app.notifications.dataKey)
|
.setGroup(app.notificationChannelsManager.data.key)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
.setContentIntent(it.getPendingIntent(app))
|
.setContentIntent(it.getPendingIntent(app))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
@ -150,7 +150,7 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
val summary = NotificationCompat.Builder(app, app.notifications.dataKey)
|
val summary = NotificationCompat.Builder(app, app.notificationChannelsManager.data.key)
|
||||||
.setContentTitle(newNotificationsText)
|
.setContentTitle(newNotificationsText)
|
||||||
.setContentText(buildSummaryText(summaryCounts))
|
.setContentText(buildSummaryText(summaryCounts))
|
||||||
.setTicker(newNotificationsText)
|
.setTicker(newNotificationsText)
|
||||||
@ -159,13 +159,13 @@ class PostNotifications(val app: App, nList: List<AppNotification>) {
|
|||||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
.setGroup(app.notifications.dataKey)
|
.setGroup(app.notificationChannelsManager.data.key)
|
||||||
.setGroupSummary(true)
|
.setGroupSummary(true)
|
||||||
.setContentIntent(summaryIntent)
|
.setContentIntent(summaryIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
notificationManager.notify(app.notifications.dataId, summary)
|
notificationManager.notify(app.notificationChannelsManager.data.id, summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -92,7 +92,7 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(app.notifications.updatesId)
|
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(app.notificationChannelsManager.updates.id)
|
||||||
|
|
||||||
val dir: File? = app.getExternalFilesDir(null)
|
val dir: File? = app.getExternalFilesDir(null)
|
||||||
if (dir?.isDirectory == true) {
|
if (dir?.isDirectory == true) {
|
||||||
|
@ -94,7 +94,7 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
|
|||||||
|
|
||||||
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
|
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
|
||||||
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
val notification = NotificationCompat.Builder(app, app.notifications.updatesKey)
|
val notification = NotificationCompat.Builder(app, app.notificationChannelsManager.updates.key)
|
||||||
.setContentTitle(app.getString(R.string.notification_updates_title))
|
.setContentTitle(app.getString(R.string.notification_updates_title))
|
||||||
.setContentText(app.getString(R.string.notification_updates_text, update.versionName))
|
.setContentText(app.getString(R.string.notification_updates_text, update.versionName))
|
||||||
.setTicker(app.getString(R.string.notification_updates_summary))
|
.setTicker(app.getString(R.string.notification_updates_summary))
|
||||||
@ -108,11 +108,11 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
|
|||||||
.setLights(0xFF00FFFF.toInt(), 2000, 2000)
|
.setLights(0xFF00FFFF.toInt(), 2000, 2000)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
.setGroup(app.notifications.updatesKey)
|
.setGroup(app.notificationChannelsManager.updates.key)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(false)
|
.setAutoCancel(false)
|
||||||
.build()
|
.build()
|
||||||
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(app.notifications.updatesId, notification)
|
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(app.notificationChannelsManager.updates.id, notification)
|
||||||
|
|
||||||
} catch (ignore: Exception) { }
|
} catch (ignore: Exception) { }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-1-8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs
|
||||||
|
|
||||||
|
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.databinding.DialogTemplateBinding
|
||||||
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class TemplateDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val onActionPerformed: (() -> Unit)? = null,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TemplateDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var b: DialogTemplateBinding
|
||||||
|
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
|
||||||
|
b = DialogTemplateBinding.inflate(activity.layoutInflater)
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(b.root)
|
||||||
|
.setPositiveButton(R.string.close) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNeutralButton(R.string.add, null)
|
||||||
|
.setOnDismissListener {
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.onClick {
|
||||||
|
// do custom action on neutral button click
|
||||||
|
// (does not dismiss the dialog)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.clickMe.onClick {
|
||||||
|
onActionPerformed?.invoke()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs.captcha
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.view.View
|
||||||
|
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.databinding.RecaptchaViewBinding
|
||||||
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class LibrusCaptchaDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val onSuccess: (recaptchaCode: String) -> Unit,
|
||||||
|
val onFailure: (() -> Unit)?,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "LibrusCaptchaDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var b: RecaptchaViewBinding
|
||||||
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
private lateinit var checkboxBackground: Drawable
|
||||||
|
private lateinit var checkboxForeground: Drawable
|
||||||
|
private var success = false
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
onShowListener?.invoke(TAG)
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
b = RecaptchaViewBinding.inflate(activity.layoutInflater)
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(b.root)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setOnDismissListener {
|
||||||
|
if (!success)
|
||||||
|
onFailure?.invoke()
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
|
||||||
|
checkboxBackground = b.checkbox.background
|
||||||
|
checkboxForeground = b.checkbox.foreground
|
||||||
|
success = false
|
||||||
|
|
||||||
|
b.root.onClick {
|
||||||
|
b.checkbox.performClick()
|
||||||
|
}
|
||||||
|
b.checkbox.onClick {
|
||||||
|
b.checkbox.background = null
|
||||||
|
b.checkbox.foreground = null
|
||||||
|
b.progress.visibility = View.VISIBLE
|
||||||
|
RecaptchaDialog(
|
||||||
|
activity,
|
||||||
|
siteKey = "6Lf48moUAAAAAB9ClhdvHr46gRWR-CN31CXQPG2U",
|
||||||
|
referer = "https://portal.librus.pl/rodzina/login",
|
||||||
|
onSuccess = { recaptchaCode ->
|
||||||
|
b.checkbox.background = checkboxBackground
|
||||||
|
b.checkbox.foreground = checkboxForeground
|
||||||
|
b.progress.visibility = View.GONE
|
||||||
|
success = true
|
||||||
|
onSuccess(recaptchaCode)
|
||||||
|
dialog.dismiss()
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
b.checkbox.background = checkboxBackground
|
||||||
|
b.checkbox.foreground = checkboxForeground
|
||||||
|
b.progress.visibility = View.GONE
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs.captcha
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Handler
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import okhttp3.*
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.RecaptchaDialogBinding
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class RecaptchaDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val siteKey: String,
|
||||||
|
val referer: String,
|
||||||
|
val autoRetry: Boolean = true,
|
||||||
|
val onSuccess: (recaptchaCode: String) -> Unit,
|
||||||
|
val onFailure: (() -> Unit)? = null,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RecaptchaDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private val b by lazy { RecaptchaDialogBinding.inflate(LayoutInflater.from(activity)) }
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
|
||||||
|
private val captchaUrl = "https://www.google.com/recaptcha/api/fallback?k=$siteKey"
|
||||||
|
private var success = false
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
private var code = ""
|
||||||
|
private var payload = ""
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
onShowListener?.invoke(TAG)
|
||||||
|
success = false
|
||||||
|
|
||||||
|
launch { initCaptcha() }
|
||||||
|
}}
|
||||||
|
|
||||||
|
private suspend fun initCaptcha() {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(captchaUrl)
|
||||||
|
.addHeader("Referer", referer)
|
||||||
|
.addHeader("Accept-Language", "pl")
|
||||||
|
.build()
|
||||||
|
app.http.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
val html = response.body()?.string() ?: return
|
||||||
|
Log.d(TAG, html)
|
||||||
|
parseHtml(html)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseHtml(html: String) {
|
||||||
|
launch {
|
||||||
|
"class=\"rc-imageselect-desc(?:-no-canonical)?\">(.+?) <strong>(.+?)</strong>".toRegex().find(html)?.let {
|
||||||
|
b.descTitle.text = it.groupValues[1]
|
||||||
|
b.descText.text = it.groupValues[2]
|
||||||
|
}
|
||||||
|
code = "name=\"c\" value=\"([A-z0-9-_]+)\"".toRegex().find(html)?.let { it.groupValues[1] } ?: return@launch
|
||||||
|
payload = "https://www.google.com/recaptcha/api2/payload?c=$code&k=$siteKey"
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(payload)
|
||||||
|
.addHeader("Referer", captchaUrl)
|
||||||
|
.addHeader("Accept-Language", "pl")
|
||||||
|
.build()
|
||||||
|
app.http.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
val bitmap: Bitmap? = BitmapFactory.decodeStream(response.body()?.byteStream())
|
||||||
|
Handler(activity.mainLooper).post {
|
||||||
|
if (bitmap == null) {
|
||||||
|
onFailure?.invoke()
|
||||||
|
Toast.makeText(activity, "Nie udało się załadować reCAPTCHA.", Toast.LENGTH_SHORT).show()
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
b.payload.setImageBitmap(bitmap)
|
||||||
|
showDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
onFailure?.invoke()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showDialog() {
|
||||||
|
if (dialog == null) {
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(b.root)
|
||||||
|
.setPositiveButton("OK") { _, _ ->
|
||||||
|
validateAnswer()
|
||||||
|
}
|
||||||
|
.setOnDismissListener {
|
||||||
|
if (!success)
|
||||||
|
onFailure?.invoke()
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
b.image0.isChecked = false
|
||||||
|
b.image1.isChecked = false
|
||||||
|
b.image2.isChecked = false
|
||||||
|
b.image3.isChecked = false
|
||||||
|
b.image4.isChecked = false
|
||||||
|
b.image5.isChecked = false
|
||||||
|
b.image6.isChecked = false
|
||||||
|
b.image7.isChecked = false
|
||||||
|
b.image8.isChecked = false
|
||||||
|
dialog!!.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateAnswer() {
|
||||||
|
launch {
|
||||||
|
val list = mutableListOf(
|
||||||
|
"c=$code"
|
||||||
|
)
|
||||||
|
if (b.image0.isChecked) list += "response=0"
|
||||||
|
if (b.image1.isChecked) list += "response=1"
|
||||||
|
if (b.image2.isChecked) list += "response=2"
|
||||||
|
if (b.image3.isChecked) list += "response=3"
|
||||||
|
if (b.image4.isChecked) list += "response=4"
|
||||||
|
if (b.image5.isChecked) list += "response=5"
|
||||||
|
if (b.image6.isChecked) list += "response=6"
|
||||||
|
if (b.image7.isChecked) list += "response=7"
|
||||||
|
if (b.image8.isChecked) list += "response=8"
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(captchaUrl)
|
||||||
|
.addHeader("Referer", captchaUrl)
|
||||||
|
.addHeader("Accept-Language", "pl")
|
||||||
|
.addHeader("Origin", "https://www.google.com")
|
||||||
|
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), list.joinToString("&")))
|
||||||
|
.build()
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
app.http.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
val html = response.body()?.string() ?: return
|
||||||
|
val match = "<textarea.+?>([A-z0-9-_]+)</textarea>".toRegex().find(html)
|
||||||
|
if (match == null) {
|
||||||
|
parseHtml(html)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Handler(activity.mainLooper).post {
|
||||||
|
success = true
|
||||||
|
onSuccess(match.groupValues[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
|||||||
val message = errors.map {
|
val message = errors.map {
|
||||||
listOf(
|
listOf(
|
||||||
it.getStringReason(activity).asBoldSpannable().asColoredSpannable(R.attr.colorOnBackground.resolveAttr(activity)),
|
it.getStringReason(activity).asBoldSpannable().asColoredSpannable(R.attr.colorOnBackground.resolveAttr(activity)),
|
||||||
|
activity.getString(R.string.error_unknown_format, it.errorCode, it.tag),
|
||||||
if (App.devMode)
|
if (App.devMode)
|
||||||
it.throwable?.stackTraceString ?: it.throwable?.localizedMessage
|
it.throwable?.stackTraceString ?: it.throwable?.localizedMessage
|
||||||
else
|
else
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -25,10 +27,14 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
|||||||
import pl.szczodrzynski.edziennik.databinding.CardHomeDebugBinding
|
import pl.szczodrzynski.edziennik.databinding.CardHomeDebugBinding
|
||||||
import pl.szczodrzynski.edziennik.dp
|
import pl.szczodrzynski.edziennik.dp
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.captcha.LibrusCaptchaDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginLibrusCaptchaActivity
|
import pl.szczodrzynski.edziennik.ui.widgets.WidgetConfigActivity
|
||||||
|
import pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||||
|
import pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
|
||||||
|
import pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class HomeDebugCard(
|
class HomeDebugCard(
|
||||||
@ -54,10 +60,6 @@ class HomeDebugCard(
|
|||||||
}
|
}
|
||||||
holder.root += b.root
|
holder.root += b.root
|
||||||
|
|
||||||
b.composeNewButton.onClick {
|
|
||||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.migrate71.onClick {
|
b.migrate71.onClick {
|
||||||
app.db.compileStatement("DELETE FROM messages WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
app.db.compileStatement("DELETE FROM messages WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||||
app.db.compileStatement("DELETE FROM messageRecipients WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
app.db.compileStatement("DELETE FROM messageRecipients WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||||
@ -84,7 +86,8 @@ class HomeDebugCard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.librusCaptchaButton.onClick {
|
b.librusCaptchaButton.onClick {
|
||||||
app.startActivity(Intent(activity, LoginLibrusCaptchaActivity::class.java))
|
//app.startActivity(Intent(activity, LoginLibrusCaptchaActivity::class.java))
|
||||||
|
LibrusCaptchaDialog(activity, onSuccess = {}, onFailure = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
b.getLogs.onClick {
|
b.getLogs.onClick {
|
||||||
@ -100,6 +103,21 @@ class HomeDebugCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.refreshWidget.onClick {
|
||||||
|
for (widgetType in 0..2) {
|
||||||
|
val theClass = when (widgetType) {
|
||||||
|
WidgetConfigActivity.WIDGET_TIMETABLE -> WidgetTimetableProvider::class.java
|
||||||
|
WidgetConfigActivity.WIDGET_NOTIFICATIONS -> WidgetNotificationsProvider::class.java
|
||||||
|
WidgetConfigActivity.WIDGET_LUCKY_NUMBER -> WidgetLuckyNumberProvider::class.java
|
||||||
|
else -> WidgetTimetableProvider::class.java
|
||||||
|
}
|
||||||
|
val intent = Intent(app, theClass)
|
||||||
|
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, AppWidgetManager.getInstance(app).getAppWidgetIds(ComponentName(app, theClass)))
|
||||||
|
app.sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
holder.root.onClick {
|
holder.root.onClick {
|
||||||
// do stuff
|
// do stuff
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,12 @@ import org.greenrobot.eventbus.Subscribe
|
|||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.ERROR_CAPTCHA_NEEDED
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_NO_ARGUMENTS
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_NO_ARGUMENTS
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
|
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.UserActionRequiredEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding
|
||||||
@ -57,7 +59,13 @@ class LoginProgressFragment : Fragment(), CoroutineScope {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doFirstLogin(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doFirstLogin(args: Bundle) {
|
||||||
launch {
|
launch {
|
||||||
|
activity.errorSnackbar.dismiss()
|
||||||
|
|
||||||
val firstProfileId = (app.db.profileDao().lastId ?: 0) + 1
|
val firstProfileId = (app.db.profileDao().lastId ?: 0) + 1
|
||||||
val loginType = args.getInt("loginType", -1)
|
val loginType = args.getInt("loginType", -1)
|
||||||
val loginMode = args.getInt("loginMode", 0)
|
val loginMode = args.getInt("loginMode", 0)
|
||||||
@ -88,6 +96,7 @@ class LoginProgressFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
activity.loginStores += event.loginStore
|
activity.loginStores += event.loginStore
|
||||||
activity.profiles += event.profileList.map { LoginSummaryProfileAdapter.Item(it) }
|
activity.profiles += event.profileList.map { LoginSummaryProfileAdapter.Item(it) }
|
||||||
|
activity.errorSnackbar.dismiss()
|
||||||
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions)
|
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +107,24 @@ class LoginProgressFragment : Fragment(), CoroutineScope {
|
|||||||
nav.navigateUp()
|
nav.navigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
fun onUserActionRequiredEvent(event: UserActionRequiredEvent) {
|
||||||
|
val args = arguments ?: run {
|
||||||
|
activity.error(ApiError(TAG, LOGIN_NO_ARGUMENTS))
|
||||||
|
nav.navigateUp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.userActionManager.execute(activity, event.profileId, event.type, onSuccess = { code ->
|
||||||
|
args.putString("recaptchaCode", code)
|
||||||
|
args.putLong("recaptchaTime", System.currentTimeMillis())
|
||||||
|
doFirstLogin(args)
|
||||||
|
}, onFailure = {
|
||||||
|
activity.error(ApiError(TAG, ERROR_CAPTCHA_NEEDED))
|
||||||
|
nav.navigateUp()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
EventBus.getDefault().register(this)
|
EventBus.getDefault().register(this)
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
@ -767,7 +767,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
|||||||
.color(IconicsColor.colorInt(iconColor))
|
.color(IconicsColor.colorInt(iconColor))
|
||||||
)
|
)
|
||||||
.setOnClickAction(() -> {
|
.setOnClickAction(() -> {
|
||||||
String channel = app.getNotifications().getDataKey();
|
String channel = app.getNotificationChannelsManager().getData().getKey();
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-1-8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Checkable;
|
||||||
|
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
|
||||||
|
public class CheckableImageView extends AppCompatImageView implements Checkable {
|
||||||
|
|
||||||
|
private boolean checked;
|
||||||
|
private boolean broadcasting;
|
||||||
|
|
||||||
|
private OnCheckedChangeListener onCheckedChangeListener;
|
||||||
|
|
||||||
|
private static final int[] CHECKED_STATE_SET = {
|
||||||
|
android.R.attr.state_checked
|
||||||
|
};
|
||||||
|
|
||||||
|
public interface OnCheckedChangeListener {
|
||||||
|
void onCheckedChanged(CheckableImageView checkableImageView, boolean isChecked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckableImageView(final Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckableImageView(final Context context, final AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckableImageView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
setOnClickListener(v -> toggle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int[] onCreateDrawableState(final int extraSpace) {
|
||||||
|
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||||
|
if (isChecked()) {
|
||||||
|
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||||
|
}
|
||||||
|
return drawableState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void toggle() {
|
||||||
|
setChecked(!checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public boolean isChecked() {
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void setChecked(final boolean checked) {
|
||||||
|
if (this.checked != checked) {
|
||||||
|
this.checked = checked;
|
||||||
|
refreshDrawableState();
|
||||||
|
|
||||||
|
// Avoid infinite recursions if setChecked() is called from a listener
|
||||||
|
if (broadcasting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
broadcasting = true;
|
||||||
|
if (onCheckedChangeListener != null) {
|
||||||
|
onCheckedChangeListener.onCheckedChanged(this, checked);
|
||||||
|
}
|
||||||
|
broadcasting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnCheckedChangeListener( final OnCheckedChangeListener onCheckedChangeListener) {
|
||||||
|
this.onCheckedChangeListener = onCheckedChangeListener;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.utils.managers
|
||||||
|
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.app.NotificationCompat.*
|
||||||
|
import androidx.core.app.NotificationManagerCompat.*
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
|
||||||
|
class NotificationChannelsManager(val c: Context) {
|
||||||
|
data class Channel(
|
||||||
|
val id: Int,
|
||||||
|
val key: String,
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val importance: Int,
|
||||||
|
val priority: Int,
|
||||||
|
val quiet: Boolean = false,
|
||||||
|
val lightColor: Int? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
fun registerAllChannels() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||||
|
return
|
||||||
|
val manager = c.getSystemService(NotificationManager::class.java)
|
||||||
|
|
||||||
|
val registered = manager.notificationChannels.map { it.id }
|
||||||
|
val all = all.map { it.key }
|
||||||
|
|
||||||
|
val toRegister = all - registered
|
||||||
|
val toDelete = registered - all
|
||||||
|
|
||||||
|
for (key in toRegister) {
|
||||||
|
val channel = this.all.firstOrNull { it.key == key } ?: continue
|
||||||
|
manager.createNotificationChannel(NotificationChannel(key, channel.name, channel.importance).apply {
|
||||||
|
description = channel.description
|
||||||
|
if (channel.quiet) {
|
||||||
|
enableVibration(false)
|
||||||
|
setSound(null, null)
|
||||||
|
}
|
||||||
|
channel.lightColor?.let {
|
||||||
|
enableLights(true)
|
||||||
|
lightColor = it
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in toDelete) {
|
||||||
|
manager.deleteNotificationChannel(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val sync by lazy {
|
||||||
|
Channel(
|
||||||
|
1,
|
||||||
|
"pl.szczodrzynski.edziennik.SYNC",
|
||||||
|
c.getString(R.string.notification_channel_get_data_name),
|
||||||
|
c.getString(R.string.notification_channel_get_data_desc),
|
||||||
|
IMPORTANCE_MIN,
|
||||||
|
PRIORITY_MIN,
|
||||||
|
quiet = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val data by lazy {
|
||||||
|
Channel(
|
||||||
|
50,
|
||||||
|
"pl.szczodrzynski.edziennik.DATA",
|
||||||
|
c.getString(R.string.notification_channel_notifications_name),
|
||||||
|
c.getString(R.string.notification_channel_notifications_desc),
|
||||||
|
IMPORTANCE_HIGH,
|
||||||
|
PRIORITY_MAX,
|
||||||
|
lightColor = 0xff2196f3.toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataQuiet by lazy {
|
||||||
|
Channel(
|
||||||
|
60,
|
||||||
|
"pl.szczodrzynski.edziennik.DATA_QUIET",
|
||||||
|
c.getString(R.string.notification_channel_notifications_quiet_name),
|
||||||
|
c.getString(R.string.notification_channel_notifications_quiet_desc),
|
||||||
|
IMPORTANCE_LOW,
|
||||||
|
PRIORITY_LOW,
|
||||||
|
quiet = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updates by lazy {
|
||||||
|
Channel(
|
||||||
|
100,
|
||||||
|
"pl.szczodrzynski.edziennik.UPDATES",
|
||||||
|
c.getString(R.string.notification_channel_updates_name),
|
||||||
|
c.getString(R.string.notification_channel_updates_desc),
|
||||||
|
IMPORTANCE_DEFAULT,
|
||||||
|
PRIORITY_DEFAULT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val userAttention by lazy {
|
||||||
|
Channel(
|
||||||
|
200,
|
||||||
|
"pl.szczodrzynski.edziennik.USER_ATTENTION",
|
||||||
|
c.getString(R.string.notification_channel_user_attention_name),
|
||||||
|
c.getString(R.string.notification_channel_user_attention_desc),
|
||||||
|
IMPORTANCE_DEFAULT,
|
||||||
|
PRIORITY_DEFAULT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val all by lazy { listOf(
|
||||||
|
sync,
|
||||||
|
data,
|
||||||
|
dataQuiet,
|
||||||
|
updates,
|
||||||
|
userAttention
|
||||||
|
) }
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-15.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.utils.managers
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.ERROR_CAPTCHA_LIBRUS_PORTAL
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.UserActionRequiredEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.captcha.LibrusCaptchaDialog
|
||||||
|
|
||||||
|
class UserActionManager(val app: App) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UserActionManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requiresUserAction(apiError: ApiError): Boolean {
|
||||||
|
return apiError.errorCode == ERROR_CAPTCHA_LIBRUS_PORTAL
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendToUser(apiError: ApiError) {
|
||||||
|
val type = when (apiError.errorCode) {
|
||||||
|
ERROR_CAPTCHA_LIBRUS_PORTAL -> UserActionRequiredEvent.CAPTCHA_LIBRUS
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EventBus.getDefault().hasSubscriberForEvent(UserActionRequiredEvent::class.java)) {
|
||||||
|
EventBus.getDefault().post(UserActionRequiredEvent(apiError.profileId ?: -1, type))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val manager = app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
|
val text = app.getString(when (type) {
|
||||||
|
UserActionRequiredEvent.CAPTCHA_LIBRUS -> R.string.notification_user_action_required_captcha_librus
|
||||||
|
else -> R.string.notification_user_action_required_text
|
||||||
|
}, apiError.profileId)
|
||||||
|
|
||||||
|
val intent = Intent(
|
||||||
|
app,
|
||||||
|
MainActivity::class.java,
|
||||||
|
"action" to "userActionRequired",
|
||||||
|
"profileId" to (apiError.profileId ?: -1),
|
||||||
|
"type" to type
|
||||||
|
)
|
||||||
|
val pendingIntent = PendingIntent.getActivity(app, System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_ONE_SHOT)
|
||||||
|
|
||||||
|
val notification = NotificationCompat.Builder(app, app.notificationChannelsManager.userAttention.key)
|
||||||
|
.setContentTitle(app.getString(R.string.notification_user_action_required_title))
|
||||||
|
.setContentText(text)
|
||||||
|
.setSmallIcon(R.drawable.ic_error_outline)
|
||||||
|
.setStyle(NotificationCompat.BigTextStyle().bigText(text))
|
||||||
|
.setColor(0xff2196f3.toInt())
|
||||||
|
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
manager.notify(System.currentTimeMillis().toInt(), notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun execute(
|
||||||
|
activity: AppCompatActivity,
|
||||||
|
profileId: Int?,
|
||||||
|
type: Int,
|
||||||
|
onSuccess: ((code: String) -> Unit)? = null,
|
||||||
|
onFailure: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
if (type != UserActionRequiredEvent.CAPTCHA_LIBRUS)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (profileId == null)
|
||||||
|
return
|
||||||
|
// show captcha dialog
|
||||||
|
// use passed onSuccess listener, else sync profile
|
||||||
|
LibrusCaptchaDialog(activity, onSuccess = onSuccess ?: { code ->
|
||||||
|
EdziennikTask.syncProfile(profileId, arguments = JsonObject(
|
||||||
|
"recaptchaCode" to code,
|
||||||
|
"recaptchaTime" to System.currentTimeMillis()
|
||||||
|
)).enqueue(activity)
|
||||||
|
}, onFailure = onFailure)
|
||||||
|
}
|
||||||
|
}
|
BIN
app/src/main/res/drawable-xhdpi/recaptcha.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/recaptcha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
15
app/src/main/res/drawable/recaptcha_card_border.xml
Normal file
15
app/src/main/res/drawable/recaptcha_card_border.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#d3ffffff" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="4dp"
|
||||||
|
android:bottomRightRadius="4dp"
|
||||||
|
android:topLeftRadius="4dp"
|
||||||
|
android:topRightRadius="4dp" />
|
||||||
|
</shape>
|
BIN
app/src/main/res/drawable/recaptcha_check.png
Normal file
BIN
app/src/main/res/drawable/recaptcha_check.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
11
app/src/main/res/drawable/recaptcha_checkbox_border.xml
Normal file
11
app/src/main/res/drawable/recaptcha_checkbox_border.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_enabled="true" android:drawable="@drawable/recaptcha_checkbox_border_normal" />
|
||||||
|
<item android:state_hovered="true" android:drawable="@drawable/recaptcha_checkbox_border_hovered" />
|
||||||
|
<item android:state_focused="true" android:drawable="@drawable/recaptcha_checkbox_border_focused" />
|
||||||
|
<item android:state_active="true" android:drawable="@drawable/recaptcha_checkbox_border_active" />
|
||||||
|
</selector>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#c1c1c1" />
|
||||||
|
<solid android:color="#ebebeb" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="2dp"
|
||||||
|
android:bottomRightRadius="2dp"
|
||||||
|
android:topLeftRadius="2dp"
|
||||||
|
android:topRightRadius="2dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#4d90fe" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="2dp"
|
||||||
|
android:bottomRightRadius="2dp"
|
||||||
|
android:topLeftRadius="2dp"
|
||||||
|
android:topRightRadius="2dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#b2b2b2" />
|
||||||
|
<solid android:color="#e0e0e0" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="2dp"
|
||||||
|
android:bottomRightRadius="2dp"
|
||||||
|
android:topLeftRadius="2dp"
|
||||||
|
android:topRightRadius="2dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#c1c1c1" />
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="2dp"
|
||||||
|
android:bottomRightRadius="2dp"
|
||||||
|
android:topLeftRadius="2dp"
|
||||||
|
android:topRightRadius="2dp" />
|
||||||
|
</shape>
|
9
app/src/main/res/drawable/recaptcha_image.xml
Normal file
9
app/src/main/res/drawable/recaptcha_image.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checkable="true" android:drawable="@drawable/recaptcha_image_pressed" /> <!-- pressed -->
|
||||||
|
<item android:state_checked="true" android:drawable="@drawable/recaptcha_image_checked" /> <!-- focused -->
|
||||||
|
</selector>
|
19
app/src/main/res/drawable/recaptcha_image_checked.xml
Normal file
19
app/src/main/res/drawable/recaptcha_image_checked.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#7fffffff"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<inset android:drawable="@drawable/recaptcha_check"
|
||||||
|
android:insetLeft="45dp"
|
||||||
|
android:insetBottom="45dp"
|
||||||
|
android:insetRight="45dp"
|
||||||
|
android:insetTop="45dp"/>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
8
app/src/main/res/drawable/recaptcha_image_pressed.xml
Normal file
8
app/src/main/res/drawable/recaptcha_image_pressed.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#30000000"/>
|
||||||
|
</shape>
|
10
app/src/main/res/layout/captcha_dialog_librus.xml
Normal file
10
app/src/main/res/layout/captcha_dialog_librus.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-8.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -32,6 +32,13 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Librus Captcha" />
|
android:text="Librus Captcha" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/refreshWidget"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Refresh all widgets" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -51,13 +58,6 @@
|
|||||||
android:text="Messages"
|
android:text="Messages"
|
||||||
android:textSize="16sp"/>
|
android:textSize="16sp"/>
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/composeNewButton"
|
|
||||||
style="@style/Widget.MaterialComponents.Button"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Compose 2" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/migrate71"
|
android:id="@+id/migrate71"
|
||||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
25
app/src/main/res/layout/dialog_template.xml
Normal file
25
app/src/main/res/layout/dialog_template.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-8.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingTop="24dp"
|
||||||
|
android:paddingRight="16dp">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hello world"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/clickMe"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Click me"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
193
app/src/main/res/layout/recaptcha_dialog.xml
Normal file
193
app/src/main/res/layout/recaptcha_dialog.xml
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="386dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="113dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:background="#4a90e2"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/descTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Wybierz wszystkie zdjęcia, na których jest"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@android:color/white" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/descText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="pan fotograf"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/payload"
|
||||||
|
android:layout_width="386dp"
|
||||||
|
android:layout_height="386dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:srcCompat="@tools:sample/backgrounds/scenic[1]" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="386dp"
|
||||||
|
android:layout_height="386dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image0"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image1"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image2"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image3"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image4"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image5"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image6"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image7"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="4dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?colorSurface" />
|
||||||
|
|
||||||
|
<pl.szczodrzynski.edziennik.utils.CheckableImageView
|
||||||
|
android:id="@+id/image8"
|
||||||
|
android:layout_width="126dp"
|
||||||
|
android:layout_height="126dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="@drawable/recaptcha_image"
|
||||||
|
android:focusable="true" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
100
app/src/main/res/layout/recaptcha_view.xml
Normal file
100
app/src/main/res/layout/recaptcha_view.xml
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-1-7.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingTop="24dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="304dp"
|
||||||
|
android:layout_height="78dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
app:cardBackgroundColor="#f9f9f9"
|
||||||
|
app:cardForegroundColor="@android:color/transparent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="28dp"
|
||||||
|
android:layout_height="28dp"
|
||||||
|
android:layout_marginLeft="12dp"
|
||||||
|
android:layout_marginRight="12dp"
|
||||||
|
android:background="@drawable/recaptcha_checkbox_border"
|
||||||
|
android:foreground="?selectableItemBackgroundBorderless">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="152dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:text="Nie jestem robotem"
|
||||||
|
android:textColor="@android:color/black"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingEnd="13dp"
|
||||||
|
android:paddingRight="13dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="end">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="26dp"
|
||||||
|
android:layout_marginStart="26dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginLeft="13dp"
|
||||||
|
android:layout_marginRight="13dp"
|
||||||
|
app:srcCompat="@drawable/recaptcha"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:text="reCAPTCHA"
|
||||||
|
android:textColor="#555"
|
||||||
|
android:textSize="10sp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Prywatność - Warunki"
|
||||||
|
android:textColor="#555"
|
||||||
|
android:textSize="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/recaptcha_card_border"/>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
@ -33,6 +33,9 @@
|
|||||||
|
|
||||||
<string name="error_115" translatable="false">ERROR_NO_STUDENTS_IN_ACCOUNT</string>
|
<string name="error_115" translatable="false">ERROR_NO_STUDENTS_IN_ACCOUNT</string>
|
||||||
|
|
||||||
|
<string name="error_3000" translatable="false">ERROR_CAPTCHA_NEEDED</string>
|
||||||
|
<string name="error_3001" translatable="false">ERROR_CAPTCHA_LIBRUS_PORTAL</string>
|
||||||
|
|
||||||
<string name="error_120" translatable="false">CODE_INTERNAL_LIBRUS_ACCOUNT_410</string>
|
<string name="error_120" translatable="false">CODE_INTERNAL_LIBRUS_ACCOUNT_410</string>
|
||||||
<string name="error_121" translatable="false">CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED</string>
|
<string name="error_121" translatable="false">CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED</string>
|
||||||
<string name="error_124" translatable="false">ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED</string>
|
<string name="error_124" translatable="false">ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED</string>
|
||||||
@ -200,6 +203,9 @@
|
|||||||
|
|
||||||
<string name="error_115_reason">Brak uczniów przypisanych do konta</string>
|
<string name="error_115_reason">Brak uczniów przypisanych do konta</string>
|
||||||
|
|
||||||
|
<string name="error_3000_reason">Wymagane rozwiązanie zadania Captcha</string>
|
||||||
|
<string name="error_3001_reason">Librus: wymagane rozwiązanie zadania Captcha</string>
|
||||||
|
|
||||||
<string name="error_120_reason">CODE_INTERNAL_LIBRUS_ACCOUNT_410</string>
|
<string name="error_120_reason">CODE_INTERNAL_LIBRUS_ACCOUNT_410</string>
|
||||||
<string name="error_121_reason">CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED</string>
|
<string name="error_121_reason">CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED</string>
|
||||||
<string name="error_124_reason">Wymagane wypełnienie CAPTCHA</string>
|
<string name="error_124_reason">Wymagane wypełnienie CAPTCHA</string>
|
||||||
|
@ -1176,4 +1176,9 @@
|
|||||||
<string name="timetable_generate_show_teachers_names">Pokaż imiona i nazwiska nauczycieli</string>
|
<string name="timetable_generate_show_teachers_names">Pokaż imiona i nazwiska nauczycieli</string>
|
||||||
<string name="messages_compose_confirm_title">Potwierdź wysłanie wiadomości</string>
|
<string name="messages_compose_confirm_title">Potwierdź wysłanie wiadomości</string>
|
||||||
<string name="messages_compose_confirm_text">Czy na pewno chcesz wysłać wiadomość do wybranych odbiorców?</string>
|
<string name="messages_compose_confirm_text">Czy na pewno chcesz wysłać wiadomość do wybranych odbiorców?</string>
|
||||||
|
<string name="notification_channel_user_attention_name">Wymagane działanie</string>
|
||||||
|
<string name="notification_channel_user_attention_desc">Powiadomienia o problemie, który wymaga działania użytkownika (np. weryfikacja Captcha). Zalecane jest pozostawienie tej kategorii włączonej.</string>
|
||||||
|
<string name="notification_user_action_required_title">Wymagane działanie w aplikacji</string>
|
||||||
|
<string name="notification_user_action_required_text">Problem, który uniemożliwia synchronizację musi być rozwiązany przez użytkownika. Kliknij, aby uzyskać więcej informacji.</string>
|
||||||
|
<string name="notification_user_action_required_captcha_librus">Librus: wymagane rozwiązanie zadania Captcha. Kliknij, aby kontynuować logowanie do dziennika.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user