forked from github/szkolny
[API] Add Vulcan OneDrive attachment downloading. Add asking for permissions on demand.
This commit is contained in:
parent
0327ba37f1
commit
d56afb034b
10
.idea/jarRepositories.xml
generated
10
.idea/jarRepositories.xml
generated
@ -41,5 +41,15 @@
|
|||||||
<option name="name" value="MavenRepo" />
|
<option name="name" value="MavenRepo" />
|
||||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven4" />
|
||||||
|
<option name="name" value="maven4" />
|
||||||
|
<option name="url" value="https://dl.bintray.com/undervoid/Powerpermission" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven4" />
|
||||||
|
<option name="name" value="maven4" />
|
||||||
|
<option name="url" value="https://dl.bintray.com/undervoid/PowerPermission" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -198,6 +198,9 @@ dependencies {
|
|||||||
kapt project(":codegen")
|
kapt project(":codegen")
|
||||||
|
|
||||||
implementation 'com.google.android:flexbox:2.0.1'
|
implementation 'com.google.android:flexbox:2.0.1'
|
||||||
|
|
||||||
|
implementation 'com.qifan.powerpermission:powerpermission:1.0.0'
|
||||||
|
implementation 'com.qifan.powerpermission:powerpermission-coroutines:1.0.0'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<!-- PowerPermission uses minSdk 21, it's safe to override as it is used only in >= 23 -->
|
||||||
|
<uses-sdk tools:overrideLibrary="com.qifan.powerpermission.coroutines, com.qifan.powerpermission.core" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -65,6 +65,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
|||||||
val gradesManager by lazy { GradesManager(this) }
|
val gradesManager by lazy { GradesManager(this) }
|
||||||
val timetableManager by lazy { TimetableManager(this) }
|
val timetableManager by lazy { TimetableManager(this) }
|
||||||
val eventManager by lazy { EventManager(this) }
|
val eventManager by lazy { EventManager(this) }
|
||||||
|
val permissionManager by lazy { PermissionManager(this) }
|
||||||
|
|
||||||
val db
|
val db
|
||||||
get() = App.db
|
get() = App.db
|
||||||
|
@ -414,8 +414,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
R.color.md_green_500
|
R.color.md_green_500
|
||||||
)
|
)
|
||||||
|
|
||||||
isStoragePermissionGranted()
|
|
||||||
|
|
||||||
SyncWorker.scheduleNext(app)
|
SyncWorker.scheduleNext(app)
|
||||||
UpdateWorker.scheduleNext(app)
|
UpdateWorker.scheduleNext(app)
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@ const val ERROR_LOGIN_VULCAN_NO_PUPILS = 331
|
|||||||
const val ERROR_VULCAN_API_MAINTENANCE = 340
|
const val ERROR_VULCAN_API_MAINTENANCE = 340
|
||||||
const val ERROR_VULCAN_API_BAD_REQUEST = 341
|
const val ERROR_VULCAN_API_BAD_REQUEST = 341
|
||||||
const val ERROR_VULCAN_API_OTHER = 342
|
const val ERROR_VULCAN_API_OTHER = 342
|
||||||
|
const val ERROR_VULCAN_ATTACHMENT_DOWNLOAD = 343
|
||||||
|
|
||||||
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401
|
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401
|
||||||
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402
|
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402
|
||||||
@ -207,5 +208,6 @@ const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
|
|||||||
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
|
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
|
||||||
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
|
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
|
||||||
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
|
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
|
||||||
|
const val ERROR_ONEDRIVE_DOWNLOAD = 930
|
||||||
|
|
||||||
const val LOGIN_NO_ARGUMENTS = 1201
|
const val LOGIN_NO_ARGUMENTS = 1201
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.edziennik.helper
|
||||||
|
|
||||||
|
import im.wangchao.mhttp.Request
|
||||||
|
import im.wangchao.mhttp.Response
|
||||||
|
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||||
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.ERROR_ONEDRIVE_DOWNLOAD
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.SYSTEM_USER_AGENT
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class OneDriveDownloadAttachment(
|
||||||
|
app: App,
|
||||||
|
fileUrl: String,
|
||||||
|
val onSuccess: (file: File) -> Unit,
|
||||||
|
val onProgress: (written: Long, total: Long) -> Unit,
|
||||||
|
val onError: (apiError: ApiError) -> Unit
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "OneDriveDownloadAttachment"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
Request.builder()
|
||||||
|
.url(fileUrl)
|
||||||
|
.userAgent(SYSTEM_USER_AGENT)
|
||||||
|
.withClient(app.httpLazy)
|
||||||
|
.callback(object : TextCallbackHandler() {
|
||||||
|
override fun onSuccess(text: String, response: Response) {
|
||||||
|
val location = response.headers().get("Location")
|
||||||
|
if (location?.contains("onedrive.live.com/redir?resid=") != true) {
|
||||||
|
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
|
||||||
|
.withApiResponse(text)
|
||||||
|
.withResponse(response))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val url = location
|
||||||
|
.replace("onedrive.live.com/redir?resid=", "storage.live.com/items/")
|
||||||
|
.replace("&", "?")
|
||||||
|
downloadFile(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(response: Response, throwable: Throwable) {
|
||||||
|
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||||
|
.withResponse(response)
|
||||||
|
.withThrowable(throwable))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
.enqueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadFile(url: String) {
|
||||||
|
val targetFile = Utils.getStorageDir()
|
||||||
|
|
||||||
|
val callback = object : FileCallbackHandler(targetFile) {
|
||||||
|
override fun onSuccess(file: File?, response: Response?) {
|
||||||
|
if (file == null) {
|
||||||
|
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
|
||||||
|
.withResponse(response))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
onSuccess(file)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
|
||||||
|
.withResponse(response)
|
||||||
|
.withThrowable(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
|
||||||
|
try {
|
||||||
|
this@OneDriveDownloadAttachment.onProgress(bytesWritten, bytesTotal)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
|
||||||
|
.withThrowable(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||||
|
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||||
|
.withResponse(response)
|
||||||
|
.withThrowable(throwable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Request.builder()
|
||||||
|
.url(url)
|
||||||
|
.userAgent(SYSTEM_USER_AGENT)
|
||||||
|
.callback(callback)
|
||||||
|
.build()
|
||||||
|
.enqueue()
|
||||||
|
}
|
||||||
|
}
|
@ -54,13 +54,13 @@ class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
|
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
|
||||||
sandboxGet(LibrusMessagesGetAttachment.TAG, "CSCheckKey",
|
sandboxGet(TAG, "CSCheckKey",
|
||||||
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
|
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
|
||||||
|
|
||||||
when (json.getString("status")) {
|
when (json.getString("status")) {
|
||||||
"not_downloaded_yet" -> {
|
"not_downloaded_yet" -> {
|
||||||
if (getAttachmentCheckKeyTries++ > 5) {
|
if (getAttachmentCheckKeyTries++ > 5) {
|
||||||
data.error(ApiError(LibrusMessagesGetAttachment.TAG, ERROR_FILE_DOWNLOAD)
|
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||||
.withApiResponse(json))
|
.withApiResponse(json))
|
||||||
return@sandboxGet
|
return@sandboxGet
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
data.error(ApiError(LibrusMessagesGetAttachment.TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||||
.withApiResponse(json))
|
.withApiResponse(json))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
|
|||||||
private fun downloadAttachment(url: String, method: Int = GET) {
|
private fun downloadAttachment(url: String, method: Int = GET) {
|
||||||
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
||||||
|
|
||||||
sandboxGetFile(LibrusMessagesGetAttachment.TAG, url, targetFile, { file ->
|
sandboxGetFile(TAG, url, targetFile, { file ->
|
||||||
|
|
||||||
val event = AttachmentGetEvent(
|
val event = AttachmentGetEvent(
|
||||||
profileId,
|
profileId,
|
||||||
|
@ -7,28 +7,29 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan
|
|||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.helper.OneDriveDownloadAttachment
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanData
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanData
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiAttachments
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiAttachments
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiSendMessage
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiSendMessage
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin.VulcanFirstLogin
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin.VulcanFirstLogin
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLogin
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLogin
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.prepareFor
|
|
||||||
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||||
companion object {
|
companion object {
|
||||||
@ -128,8 +129,51 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
|
|
||||||
override fun markAllAnnouncementsAsRead() {}
|
override fun markAllAnnouncementsAsRead() {}
|
||||||
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
||||||
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
|
|
||||||
override fun getRecipientList() {}
|
override fun getRecipientList() {}
|
||||||
|
|
||||||
|
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
|
||||||
|
val fileUrl = attachmentName.substringAfter(":")
|
||||||
|
if (attachmentName == fileUrl) {
|
||||||
|
data.error(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
OneDriveDownloadAttachment(
|
||||||
|
app,
|
||||||
|
fileUrl,
|
||||||
|
onSuccess = { file ->
|
||||||
|
val event = AttachmentGetEvent(
|
||||||
|
data.profileId,
|
||||||
|
owner,
|
||||||
|
attachmentId,
|
||||||
|
AttachmentGetEvent.TYPE_FINISHED,
|
||||||
|
file.absolutePath
|
||||||
|
)
|
||||||
|
|
||||||
|
val attachmentDataFile = File(Utils.getStorageDir(), ".${data.profileId}_${event.ownerId}_${event.attachmentId}")
|
||||||
|
Utils.writeStringToFile(attachmentDataFile, event.fileName)
|
||||||
|
|
||||||
|
EventBus.getDefault().postSticky(event)
|
||||||
|
|
||||||
|
completed()
|
||||||
|
},
|
||||||
|
onProgress = { written, total ->
|
||||||
|
val event = AttachmentGetEvent(
|
||||||
|
data.profileId,
|
||||||
|
owner,
|
||||||
|
attachmentId,
|
||||||
|
AttachmentGetEvent.TYPE_PROGRESS,
|
||||||
|
bytesWritten = written
|
||||||
|
)
|
||||||
|
|
||||||
|
EventBus.getDefault().postSticky(event)
|
||||||
|
},
|
||||||
|
onError = { apiError ->
|
||||||
|
data.error(apiError)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getEvent(eventFull: EventFull) {
|
override fun getEvent(eventFull: EventFull) {
|
||||||
login(LOGIN_METHOD_VULCAN_API) {
|
login(LOGIN_METHOD_VULCAN_API) {
|
||||||
val list = data.app.db.eventDao().getAllNow(data.profileId).filter { !it.addedManually }
|
val list = data.app.db.eventDao().getAllNow(data.profileId).filter { !it.addedManually }
|
||||||
|
@ -17,6 +17,7 @@ import com.google.gson.JsonObject
|
|||||||
import pl.droidsonroids.gif.GifDrawable
|
import pl.droidsonroids.gif.GifDrawable
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
|
||||||
|
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.navlib.ImageHolder
|
import pl.szczodrzynski.navlib.ImageHolder
|
||||||
import pl.szczodrzynski.navlib.R
|
import pl.szczodrzynski.navlib.R
|
||||||
@ -128,7 +129,7 @@ open class Profile(
|
|||||||
override fun getImageHolder(context: Context): ImageHolder {
|
override fun getImageHolder(context: Context): ImageHolder {
|
||||||
return if (!image.isNullOrEmpty()) {
|
return if (!image.isNullOrEmpty()) {
|
||||||
try {
|
try {
|
||||||
ImageHolder(image ?: "")
|
ProfileImageHolder(image ?: "")
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ class LoginChooserFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.devMode.visibility = if (App.debugMode) View.VISIBLE else View.GONE
|
b.devMode.visibility = if (App.debugMode) View.VISIBLE else View.GONE
|
||||||
|
b.devMode.isChecked = app.config.debugMode
|
||||||
b.devMode.onChange { v, isChecked ->
|
b.devMode.onChange { v, isChecked ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
MaterialDialog.Builder(activity)
|
MaterialDialog.Builder(activity)
|
||||||
@ -94,6 +95,7 @@ class LoginChooserFragment : Fragment() {
|
|||||||
.negativeText(R.string.no)
|
.negativeText(R.string.no)
|
||||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||||
app.config.debugMode = true
|
app.config.debugMode = true
|
||||||
|
App.devMode = true
|
||||||
MaterialAlertDialogBuilder(activity)
|
MaterialAlertDialogBuilder(activity)
|
||||||
.setTitle("Restart")
|
.setTitle("Restart")
|
||||||
.setMessage("Wymagany restart aplikacji")
|
.setMessage("Wymagany restart aplikacji")
|
||||||
@ -104,12 +106,10 @@ class LoginChooserFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.show()
|
.show()
|
||||||
/*if (b.devModeLayout.getVisibility() !== View.VISIBLE) {
|
|
||||||
Anim.expand(b.devModeTitle, 500, null)
|
|
||||||
Anim.expand(b.devModeLayout, 500, null)
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
.onNegative { _: MaterialDialog?, _: DialogAction? ->
|
.onNegative { _: MaterialDialog?, _: DialogAction? ->
|
||||||
|
app.config.debugMode = false
|
||||||
|
App.devMode = false
|
||||||
b.devMode.isChecked = app.config.debugMode
|
b.devMode.isChecked = app.config.debugMode
|
||||||
b.devMode.jumpDrawablesToCurrentState()
|
b.devMode.jumpDrawablesToCurrentState()
|
||||||
Anim.collapse(b.devMode, 1000, null)
|
Anim.collapse(b.devMode, 1000, null)
|
||||||
@ -117,6 +117,7 @@ class LoginChooserFragment : Fragment() {
|
|||||||
.show()
|
.show()
|
||||||
} else {
|
} else {
|
||||||
app.config.debugMode = false
|
app.config.debugMode = false
|
||||||
|
App.devMode = false
|
||||||
/*if (b.devModeLayout.getVisibility() === View.VISIBLE) {
|
/*if (b.devModeLayout.getVisibility() === View.VISIBLE) {
|
||||||
Anim.collapse(b.devModeTitle, 500, null)
|
Anim.collapse(b.devModeTitle, 500, null)
|
||||||
Anim.collapse(b.devModeLayout, 500, null)
|
Anim.collapse(b.devModeLayout, 500, null)
|
||||||
|
@ -58,8 +58,9 @@ class AttachmentAdapter(
|
|||||||
val item = items[position]
|
val item = items[position]
|
||||||
val b = holder.b
|
val b = holder.b
|
||||||
|
|
||||||
|
val fileName = item.name.substringBefore(":http")
|
||||||
// create an icon for the attachment
|
// create an icon for the attachment
|
||||||
val icon: IIcon = when (Utils.getExtensionFromFileName(item.name)) {
|
val icon: IIcon = when (Utils.getExtensionFromFileName(fileName)) {
|
||||||
"doc", "docx", "odt", "rtf" -> SzkolnyFont.Icon.szf_file_word_outline
|
"doc", "docx", "odt", "rtf" -> SzkolnyFont.Icon.szf_file_word_outline
|
||||||
"xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline
|
"xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline
|
||||||
"ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline
|
"ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline
|
||||||
@ -73,12 +74,12 @@ class AttachmentAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.chip.text = if (item.isDownloading) {
|
b.chip.text = if (item.isDownloading) {
|
||||||
app.getString(R.string.messages_attachment_downloading_format, item.name, item.downloadProgress)
|
app.getString(R.string.messages_attachment_downloading_format, fileName, item.downloadProgress)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
item.size?.let {
|
item.size?.let {
|
||||||
app.getString(R.string.messages_attachment_format, item.name, Utils.readableFileSize(it))
|
app.getString(R.string.messages_attachment_format, fileName, Utils.readableFileSize(it))
|
||||||
} ?: item.name
|
} ?: fileName
|
||||||
}
|
}
|
||||||
|
|
||||||
b.chip.chipIcon = IconicsDrawable(context)
|
b.chip.chipIcon = IconicsDrawable(context)
|
||||||
|
@ -8,12 +8,14 @@ import android.content.Context
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
|
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
|
||||||
@ -41,6 +43,8 @@ class AttachmentsView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun init(arguments: Bundle, owner: Any) {
|
fun init(arguments: Bundle, owner: Any) {
|
||||||
|
val app = context.applicationContext as App
|
||||||
|
val activity = context as? AppCompatActivity ?: return
|
||||||
val list = this as? RecyclerView ?: return
|
val list = this as? RecyclerView ?: return
|
||||||
|
|
||||||
val profileId = arguments.get<Int>("profileId") ?: return
|
val profileId = arguments.get<Int>("profileId") ?: return
|
||||||
@ -49,12 +53,16 @@ class AttachmentsView @JvmOverloads constructor(
|
|||||||
val attachmentSizes = arguments.getLongArray("attachmentSizes")
|
val attachmentSizes = arguments.getLongArray("attachmentSizes")
|
||||||
|
|
||||||
val adapter = AttachmentAdapter(context, onAttachmentClick = { item ->
|
val adapter = AttachmentAdapter(context, onAttachmentClick = { item ->
|
||||||
|
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
|
||||||
downloadAttachment(item)
|
downloadAttachment(item)
|
||||||
|
}
|
||||||
}, onAttachmentLongClick = { chip, item ->
|
}, onAttachmentLongClick = { chip, item ->
|
||||||
val popupMenu = PopupMenu(chip.context, chip)
|
val popupMenu = PopupMenu(chip.context, chip)
|
||||||
popupMenu.menu.add(0, 1, 0, R.string.messages_attachment_download_again)
|
popupMenu.menu.add(0, 1, 0, R.string.messages_attachment_download_again)
|
||||||
popupMenu.setOnMenuItemClickListener {
|
popupMenu.setOnMenuItemClickListener {
|
||||||
|
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
|
||||||
downloadAttachment(item, forceDownload = true)
|
downloadAttachment(item, forceDownload = true)
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
popupMenu.show()
|
popupMenu.show()
|
||||||
@ -97,7 +105,8 @@ class AttachmentsView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private fun downloadAttachment(attachment: AttachmentAdapter.Item, forceDownload: Boolean = false) {
|
private fun downloadAttachment(attachment: AttachmentAdapter.Item, forceDownload: Boolean = false) {
|
||||||
if (!forceDownload && attachment.isDownloaded) {
|
if (!forceDownload && attachment.isDownloaded) {
|
||||||
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name))
|
// open file by name, or first part before ':' (Vulcan OneDrive)
|
||||||
|
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name.substringBefore(":")))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,17 +137,19 @@ class AttachmentsView @JvmOverloads constructor(
|
|||||||
when (event.eventType) {
|
when (event.eventType) {
|
||||||
AttachmentGetEvent.TYPE_FINISHED -> {
|
AttachmentGetEvent.TYPE_FINISHED -> {
|
||||||
// save the downloaded file name
|
// save the downloaded file name
|
||||||
attachment.downloadedName = event.fileName
|
|
||||||
attachment.isDownloading = false
|
attachment.isDownloading = false
|
||||||
attachment.isDownloaded = true
|
attachment.isDownloaded = true
|
||||||
|
|
||||||
// update file name for iDziennik which
|
// get the download url before updating file name
|
||||||
// does not provide the name before downloading
|
val fileUrl = attachment.name.substringBefore(":", missingDelimiterValue = "")
|
||||||
if (!attachment.name.contains("."))
|
// update file name with the downloaded one
|
||||||
attachment.name = File(attachment.downloadedName).name
|
attachment.name = File(event.fileName).name
|
||||||
|
// save the download url back
|
||||||
|
if (fileUrl != "")
|
||||||
|
attachment.name += ":$fileUrl"
|
||||||
|
|
||||||
// open the file
|
// open the file
|
||||||
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name))
|
Utils.openFile(context, File(event.fileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachmentGetEvent.TYPE_PROGRESS -> {
|
AttachmentGetEvent.TYPE_PROGRESS -> {
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.utils
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import pl.szczodrzynski.navlib.ImageHolder
|
||||||
|
|
||||||
|
class ProfileImageHolder(url: String) : ImageHolder(url) {
|
||||||
|
|
||||||
|
override fun applyTo(imageView: ImageView, tag: String?): Boolean {
|
||||||
|
return try {
|
||||||
|
super.applyTo(imageView, tag)
|
||||||
|
} catch (_: Exception) { false }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.utils.managers
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.qifan.powerpermission.coroutines.awaitAskPermissions
|
||||||
|
import com.qifan.powerpermission.data.hasAllGranted
|
||||||
|
import com.qifan.powerpermission.data.hasPermanentDenied
|
||||||
|
import com.qifan.powerpermission.data.hasRational
|
||||||
|
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.R
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class PermissionManager(val app: App) : CoroutineScope {
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestStoragePermission(
|
||||||
|
activity: AppCompatActivity,
|
||||||
|
@StringRes permissionMessage: Int,
|
||||||
|
onSuccess: suspend CoroutineScope.() -> Unit
|
||||||
|
) {
|
||||||
|
launch {
|
||||||
|
if (isStoragePermissionGranted()) {
|
||||||
|
onSuccess()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val result = activity.awaitAskPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
when {
|
||||||
|
result.hasAllGranted() -> onSuccess()
|
||||||
|
result.hasRational() -> {
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.permissions_required)
|
||||||
|
.setMessage(permissionMessage)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
requestStoragePermission(activity, permissionMessage, 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -88,7 +88,7 @@
|
|||||||
<string name="day_today_format">today (%s)</string>
|
<string name="day_today_format">today (%s)</string>
|
||||||
<string name="day_tomorrow_format">tomorrow (%s)</string>
|
<string name="day_tomorrow_format">tomorrow (%s)</string>
|
||||||
<string name="debug_notice">You shouldn\'t really care about what you see here.</string>
|
<string name="debug_notice">You shouldn\'t really care about what you see here.</string>
|
||||||
<string name="dev_mode_enable_warning">These settings are intended for this app\'s developers, not for normal users.\n\nThey are not described in any way, so using them may lead to breaking some functions in the app, damaging your system or losing data.\n\nBe careful and use them wisely.</string>
|
<string name="dev_mode_enable_warning">These settings are intended for this app\'s developers, not for normal users.\n\nThey are not described in any way, so using them may lead to breaking some functions in the app, damaging your system, losing data or even installing a virus on the battery.\n\nBe careful and use them wisely.</string>
|
||||||
<string name="developer_mode">Developer mode</string>
|
<string name="developer_mode">Developer mode</string>
|
||||||
<string name="dialog_averages_expected_format">Predicted semester %d average:\n%#.2f\n</string>
|
<string name="dialog_averages_expected_format">Predicted semester %d average:\n%#.2f\n</string>
|
||||||
<string name="dialog_averages_expected_yearly_format">Predicted yearly average:\n%#.2f\n</string>
|
<string name="dialog_averages_expected_yearly_format">Predicted yearly average:\n%#.2f\n</string>
|
||||||
@ -431,7 +431,7 @@
|
|||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
<string name="help_notification_web_push">Notification forwarding allows you to pair a PC web browser to receive notifications on your desktop. This includes new grades, events, homework etc.\n\nClick \"Notification forwarding\" to begin.</string>
|
<string name="help_notification_web_push">Notification forwarding allows you to pair a PC web browser to receive notifications on your desktop. This includes new grades, events, homework etc.\n\nClick \"Notification forwarding\" to begin.</string>
|
||||||
<string name="help_register_agreement">Registraton will run automatically on the first login.\n\nThere will be some data sent to the app server:\n- your school and class ID\n- your e-register username\n- your first and last name\n\nThe only data visible to others is your name (when sharing events). Any private data (like password, grades etc.) won\'t be sent anywhere. Learn more in the Privacy policy.</string>
|
<string name="help_register_agreement">Registration will run automatically on the first login.\n\nThere will be some data sent to the app server:\n- your school and class ID\n- your e-register username\n- your first and last name\n\nThe only data visible to others is your name (when sharing events). Any private data (like password, grades etc.) won\'t be sent anywhere. Learn more in the Privacy policy.</string>
|
||||||
<string name="hint_download_again">Download again</string>
|
<string name="hint_download_again">Download again</string>
|
||||||
<string name="hint_edit_event">Edit event</string>
|
<string name="hint_edit_event">Edit event</string>
|
||||||
<string name="hint_go_to_timetable">Go to timetable</string>
|
<string name="hint_go_to_timetable">Go to timetable</string>
|
||||||
@ -683,9 +683,9 @@
|
|||||||
<string name="messages_draft_title">Draft</string>
|
<string name="messages_draft_title">Draft</string>
|
||||||
<string name="messages_no_data">You don\'t have any messages</string>
|
<string name="messages_no_data">You don\'t have any messages</string>
|
||||||
<string name="messages_recipient_list_download_error">Error getting the recipient list</string>
|
<string name="messages_recipient_list_download_error">Error getting the recipient list</string>
|
||||||
<string name="messages_recipients_list_read_format"><![CDATA[<li>&nbsp;%s, read: %s, %s</li>]]></string>
|
<string name="messages_recipients_list_read_format"><![CDATA[<li> %s, read: %s, %s</li>]]></string>
|
||||||
<string name="messages_recipients_list_read_unknown_date_format"><![CDATA[<li>&nbsp;%s, read: yes</li>]]></string>
|
<string name="messages_recipients_list_read_unknown_date_format"><![CDATA[<li> %s, read: yes</li>]]></string>
|
||||||
<string name="messages_recipients_list_unread_format"><![CDATA[<li>&nbsp;%s, read: <font color=red>no</font></li>]]></string>
|
<string name="messages_recipients_list_unread_format"><![CDATA[<li> %s, read: <font color=red>no</font></li>]]></string>
|
||||||
<string name="messages_reply">Reply</string>
|
<string name="messages_reply">Reply</string>
|
||||||
<string name="messages_reply_date_time_format">%s at %s</string>
|
<string name="messages_reply_date_time_format">%s at %s</string>
|
||||||
<string name="messages_search">Search</string>
|
<string name="messages_search">Search</string>
|
||||||
|
@ -128,6 +128,7 @@
|
|||||||
<string name="error_340" translatable="false">ERROR_VULCAN_API_MAINTENANCE</string>
|
<string name="error_340" translatable="false">ERROR_VULCAN_API_MAINTENANCE</string>
|
||||||
<string name="error_341" translatable="false">ERROR_VULCAN_API_BAD_REQUEST</string>
|
<string name="error_341" translatable="false">ERROR_VULCAN_API_BAD_REQUEST</string>
|
||||||
<string name="error_342" translatable="false">ERROR_VULCAN_API_OTHER</string>
|
<string name="error_342" translatable="false">ERROR_VULCAN_API_OTHER</string>
|
||||||
|
<string name="error_343" translatable="false">ERROR_VULCAN_ATTACHMENT_DOWNLOAD</string>
|
||||||
|
|
||||||
<string name="error_401" translatable="false">ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN</string>
|
<string name="error_401" translatable="false">ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN</string>
|
||||||
<string name="error_402" translatable="false">ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME</string>
|
<string name="error_402" translatable="false">ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME</string>
|
||||||
@ -177,6 +178,7 @@
|
|||||||
<string name="error_914" translatable="false">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
<string name="error_914" translatable="false">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
||||||
<string name="error_920" translatable="false">EXCEPTION_EDUDZIENNIK_WEB_REQUEST</string>
|
<string name="error_920" translatable="false">EXCEPTION_EDUDZIENNIK_WEB_REQUEST</string>
|
||||||
<string name="error_921" translatable="false">EXCEPTION_EDUDZIENNIK_FILE_REQUEST</string>
|
<string name="error_921" translatable="false">EXCEPTION_EDUDZIENNIK_FILE_REQUEST</string>
|
||||||
|
<string name="error_930" translatable="false">ERROR_ONEDRIVE_DOWNLOAD</string>
|
||||||
|
|
||||||
<string name="error_1201" translatable="false">LOGIN_NO_ARGUMENTS</string>
|
<string name="error_1201" translatable="false">LOGIN_NO_ARGUMENTS</string>
|
||||||
|
|
||||||
@ -304,6 +306,7 @@
|
|||||||
<string name="error_340_reason">Vulcan: przerwa techniczna</string>
|
<string name="error_340_reason">Vulcan: przerwa techniczna</string>
|
||||||
<string name="error_341_reason">Vulcan: błąd żądania, zgłoś błąd</string>
|
<string name="error_341_reason">Vulcan: błąd żądania, zgłoś błąd</string>
|
||||||
<string name="error_342_reason">Vulcan: inny błąd, wyślij zgłoszenie</string>
|
<string name="error_342_reason">Vulcan: inny błąd, wyślij zgłoszenie</string>
|
||||||
|
<string name="error_343_reason">Vulcan: nie znaleziono adresu załącznika</string>
|
||||||
|
|
||||||
<string name="error_401_reason">Nieprawidłowe dane logowania</string>
|
<string name="error_401_reason">Nieprawidłowe dane logowania</string>
|
||||||
<string name="error_402_reason">Nieprawidłowa nazwa szkoły</string>
|
<string name="error_402_reason">Nieprawidłowa nazwa szkoły</string>
|
||||||
@ -353,6 +356,7 @@
|
|||||||
<string name="error_914_reason">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
<string name="error_914_reason">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
||||||
<string name="error_920_reason">Wystąpił błąd</string>
|
<string name="error_920_reason">Wystąpił błąd</string>
|
||||||
<string name="error_921_reason">Wystąpił błąd podczas pobierania pliku</string>
|
<string name="error_921_reason">Wystąpił błąd podczas pobierania pliku</string>
|
||||||
|
<string name="error_930_reason">Nie udało się pobrać pliku z OneDrive</string>
|
||||||
|
|
||||||
<string name="error_1201_reason">Nie podano parametrów</string>
|
<string name="error_1201_reason">Nie podano parametrów</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<string name="messages_attachment_format" translatable="false">%s (%s)</string>
|
<string name="messages_attachment_format" translatable="false">%s (%s)</string>
|
||||||
<string name="messages_attachment_no_size_format" translatable="false">%s</string>
|
<string name="messages_attachment_no_size_format" translatable="false">%s</string>
|
||||||
<string name="messages_date_time_format" translatable="false">%s, %s</string>
|
<string name="messages_date_time_format" translatable="false">%s, %s</string>
|
||||||
<string name="messages_recipients_list_unknown_state_format" translatable="false"><![CDATA[<li>&nbsp;%s</li>]]></string>
|
<string name="messages_recipients_list_unknown_state_format" translatable="false"><![CDATA[<li> %s</li>]]></string>
|
||||||
<string name="notification_ticker_format" translatable="false">Szkolny.eu: %s</string>
|
<string name="notification_ticker_format" translatable="false">Szkolny.eu: %s</string>
|
||||||
<string name="preference_file" translatable="false">pl.szczodrzynski.edziennik_preferences</string>
|
<string name="preference_file" translatable="false">pl.szczodrzynski.edziennik_preferences</string>
|
||||||
<string name="preference_file_global" translatable="false">pl.szczodrzynski.edziennik_profiles</string>
|
<string name="preference_file_global" translatable="false">pl.szczodrzynski.edziennik_profiles</string>
|
||||||
@ -736,9 +736,9 @@
|
|||||||
<string name="messages_draft_title">Wersja robocza</string>
|
<string name="messages_draft_title">Wersja robocza</string>
|
||||||
<string name="messages_no_data">Nie masz żadnych wiadomości.</string>
|
<string name="messages_no_data">Nie masz żadnych wiadomości.</string>
|
||||||
<string name="messages_recipient_list_download_error">Błąd pobierania listy odbiorców</string>
|
<string name="messages_recipient_list_download_error">Błąd pobierania listy odbiorców</string>
|
||||||
<string name="messages_recipients_list_read_format"><![CDATA[<li>&nbsp;%s, przeczytano: %s, %s</li>]]></string>
|
<string name="messages_recipients_list_read_format"><![CDATA[<li> %s, przeczytano: %s, %s</li>]]></string>
|
||||||
<string name="messages_recipients_list_read_unknown_date_format"><![CDATA[<li>&nbsp;%s, przeczytano: tak</li>]]></string>
|
<string name="messages_recipients_list_read_unknown_date_format"><![CDATA[<li> %s, przeczytano: tak</li>]]></string>
|
||||||
<string name="messages_recipients_list_unread_format"><![CDATA[<li>&nbsp;%s, przeczytano: <font color=red>nie</font></li>]]></string>
|
<string name="messages_recipients_list_unread_format"><![CDATA[<li> %s, przeczytano: <font color=red>nie</font></li>]]></string>
|
||||||
<string name="messages_reply">Odpowiedz</string>
|
<string name="messages_reply">Odpowiedz</string>
|
||||||
<string name="messages_reply_date_time_format">%s o %s</string>
|
<string name="messages_reply_date_time_format">%s o %s</string>
|
||||||
<string name="messages_search">Szukaj</string>
|
<string name="messages_search">Szukaj</string>
|
||||||
@ -1281,4 +1281,7 @@
|
|||||||
<string name="yesterday">wczoraj</string>
|
<string name="yesterday">wczoraj</string>
|
||||||
<string name="you_are_offline_text">Jesteś offline. Spróbuj włączyć Wi-Fi lub dane komórkowe.</string>
|
<string name="you_are_offline_text">Jesteś offline. Spróbuj włączyć Wi-Fi lub dane komórkowe.</string>
|
||||||
<string name="you_are_offline_title">Połączenie sieciowe</string>
|
<string name="you_are_offline_title">Połączenie sieciowe</string>
|
||||||
|
<string name="permissions_required">Wymagane uprawnienia</string>
|
||||||
|
<string name="permissions_denied">Odmówiłeś aplikacji wymaganych uprawnień do wykonania tej czynności.\n\nAby przynać uprawnienia, otwórz sekcję Uprawnienia dla aplikacji Szkolny.eu w ustawieniach telefonu.\n\nKliknij OK, aby przejść do ustawień aplikacji.</string>
|
||||||
|
<string name="permissions_attachment">Musisz przyznać uprawnienia do zapisu plików w pamięci telefonu, aby móc pobrać załącznik.\n\nKliknij OK, aby przyznać uprawnienia.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -87,6 +87,7 @@ allprojects {
|
|||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx/" }
|
maven { url "https://kotlin.bintray.com/kotlinx/" }
|
||||||
maven { url "https://dl.bintray.com/wulkanowy/wulkanowy" }
|
maven { url "https://dl.bintray.com/wulkanowy/wulkanowy" }
|
||||||
|
maven { url "https://dl.bintray.com/undervoid/PowerPermission" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user