mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2024-11-24 10:54:36 -06:00
[API] Add Vulcan OneDrive attachment downloading. Add asking for permissions on demand.
This commit is contained in:
parent
0327ba37f1
commit
d56afb034b
@ -41,5 +41,15 @@
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</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>
|
||||
</project>
|
@ -198,6 +198,9 @@ dependencies {
|
||||
kapt project(":codegen")
|
||||
|
||||
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 {
|
||||
mavenCentral()
|
||||
|
@ -14,6 +14,9 @@
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<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
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
|
@ -65,6 +65,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
val gradesManager by lazy { GradesManager(this) }
|
||||
val timetableManager by lazy { TimetableManager(this) }
|
||||
val eventManager by lazy { EventManager(this) }
|
||||
val permissionManager by lazy { PermissionManager(this) }
|
||||
|
||||
val db
|
||||
get() = App.db
|
||||
|
@ -414,8 +414,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
R.color.md_green_500
|
||||
)
|
||||
|
||||
isStoragePermissionGranted()
|
||||
|
||||
SyncWorker.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_BAD_REQUEST = 341
|
||||
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_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_EDUDZIENNIK_WEB_REQUEST = 920
|
||||
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
|
||||
const val ERROR_ONEDRIVE_DOWNLOAD = 930
|
||||
|
||||
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) {
|
||||
sandboxGet(LibrusMessagesGetAttachment.TAG, "CSCheckKey",
|
||||
sandboxGet(TAG, "CSCheckKey",
|
||||
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
|
||||
|
||||
when (json.getString("status")) {
|
||||
"not_downloaded_yet" -> {
|
||||
if (getAttachmentCheckKeyTries++ > 5) {
|
||||
data.error(ApiError(LibrusMessagesGetAttachment.TAG, ERROR_FILE_DOWNLOAD)
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withApiResponse(json))
|
||||
return@sandboxGet
|
||||
}
|
||||
@ -75,7 +75,7 @@ class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
|
||||
}
|
||||
|
||||
else -> {
|
||||
data.error(ApiError(LibrusMessagesGetAttachment.TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
}
|
||||
@ -85,7 +85,7 @@ class LibrusSandboxDownloadAttachment(override val data: DataLibrus,
|
||||
private fun downloadAttachment(url: String, method: Int = GET) {
|
||||
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
||||
|
||||
sandboxGetFile(LibrusMessagesGetAttachment.TAG, url, targetFile, { file ->
|
||||
sandboxGetFile(TAG, url, targetFile, { file ->
|
||||
|
||||
val event = AttachmentGetEvent(
|
||||
profileId,
|
||||
|
@ -7,28 +7,29 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan
|
||||
import com.google.gson.JsonObject
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
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.api.VulcanApiAttachments
|
||||
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.firstlogin.VulcanFirstLogin
|
||||
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.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
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.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
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 {
|
||||
companion object {
|
||||
@ -128,8 +129,51 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {}
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
||||
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
|
||||
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) {
|
||||
login(LOGIN_METHOD_VULCAN_API) {
|
||||
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.szczodrzynski.edziennik.*
|
||||
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.navlib.ImageHolder
|
||||
import pl.szczodrzynski.navlib.R
|
||||
@ -128,7 +129,7 @@ open class Profile(
|
||||
override fun getImageHolder(context: Context): ImageHolder {
|
||||
return if (!image.isNullOrEmpty()) {
|
||||
try {
|
||||
ImageHolder(image ?: "")
|
||||
ProfileImageHolder(image ?: "")
|
||||
} catch (_: Exception) {
|
||||
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.isChecked = app.config.debugMode
|
||||
b.devMode.onChange { v, isChecked ->
|
||||
if (isChecked) {
|
||||
MaterialDialog.Builder(activity)
|
||||
@ -94,6 +95,7 @@ class LoginChooserFragment : Fragment() {
|
||||
.negativeText(R.string.no)
|
||||
.onPositive { _: MaterialDialog?, _: DialogAction? ->
|
||||
app.config.debugMode = true
|
||||
App.devMode = true
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle("Restart")
|
||||
.setMessage("Wymagany restart aplikacji")
|
||||
@ -104,12 +106,10 @@ class LoginChooserFragment : Fragment() {
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
/*if (b.devModeLayout.getVisibility() !== View.VISIBLE) {
|
||||
Anim.expand(b.devModeTitle, 500, null)
|
||||
Anim.expand(b.devModeLayout, 500, null)
|
||||
}*/
|
||||
}
|
||||
.onNegative { _: MaterialDialog?, _: DialogAction? ->
|
||||
app.config.debugMode = false
|
||||
App.devMode = false
|
||||
b.devMode.isChecked = app.config.debugMode
|
||||
b.devMode.jumpDrawablesToCurrentState()
|
||||
Anim.collapse(b.devMode, 1000, null)
|
||||
@ -117,6 +117,7 @@ class LoginChooserFragment : Fragment() {
|
||||
.show()
|
||||
} else {
|
||||
app.config.debugMode = false
|
||||
App.devMode = false
|
||||
/*if (b.devModeLayout.getVisibility() === View.VISIBLE) {
|
||||
Anim.collapse(b.devModeTitle, 500, null)
|
||||
Anim.collapse(b.devModeLayout, 500, null)
|
||||
|
@ -58,8 +58,9 @@ class AttachmentAdapter(
|
||||
val item = items[position]
|
||||
val b = holder.b
|
||||
|
||||
val fileName = item.name.substringBefore(":http")
|
||||
// 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
|
||||
"xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline
|
||||
"ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline
|
||||
@ -73,12 +74,12 @@ class AttachmentAdapter(
|
||||
}
|
||||
|
||||
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 {
|
||||
item.size?.let {
|
||||
app.getString(R.string.messages_attachment_format, item.name, Utils.readableFileSize(it))
|
||||
} ?: item.name
|
||||
app.getString(R.string.messages_attachment_format, fileName, Utils.readableFileSize(it))
|
||||
} ?: fileName
|
||||
}
|
||||
|
||||
b.chip.chipIcon = IconicsDrawable(context)
|
||||
|
@ -8,12 +8,14 @@ import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
|
||||
@ -41,6 +43,8 @@ class AttachmentsView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
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 profileId = arguments.get<Int>("profileId") ?: return
|
||||
@ -49,12 +53,16 @@ class AttachmentsView @JvmOverloads constructor(
|
||||
val attachmentSizes = arguments.getLongArray("attachmentSizes")
|
||||
|
||||
val adapter = AttachmentAdapter(context, onAttachmentClick = { item ->
|
||||
downloadAttachment(item)
|
||||
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
|
||||
downloadAttachment(item)
|
||||
}
|
||||
}, onAttachmentLongClick = { chip, item ->
|
||||
val popupMenu = PopupMenu(chip.context, chip)
|
||||
popupMenu.menu.add(0, 1, 0, R.string.messages_attachment_download_again)
|
||||
popupMenu.setOnMenuItemClickListener {
|
||||
downloadAttachment(item, forceDownload = true)
|
||||
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
|
||||
downloadAttachment(item, forceDownload = true)
|
||||
}
|
||||
true
|
||||
}
|
||||
popupMenu.show()
|
||||
@ -97,7 +105,8 @@ class AttachmentsView @JvmOverloads constructor(
|
||||
|
||||
private fun downloadAttachment(attachment: AttachmentAdapter.Item, forceDownload: Boolean = false) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -128,17 +137,19 @@ class AttachmentsView @JvmOverloads constructor(
|
||||
when (event.eventType) {
|
||||
AttachmentGetEvent.TYPE_FINISHED -> {
|
||||
// save the downloaded file name
|
||||
attachment.downloadedName = event.fileName
|
||||
attachment.isDownloading = false
|
||||
attachment.isDownloaded = true
|
||||
|
||||
// update file name for iDziennik which
|
||||
// does not provide the name before downloading
|
||||
if (!attachment.name.contains("."))
|
||||
attachment.name = File(attachment.downloadedName).name
|
||||
// get the download url before updating file name
|
||||
val fileUrl = attachment.name.substringBefore(":", missingDelimiterValue = "")
|
||||
// update file name with the downloaded one
|
||||
attachment.name = File(event.fileName).name
|
||||
// save the download url back
|
||||
if (fileUrl != "")
|
||||
attachment.name += ":$fileUrl"
|
||||
|
||||
// open the file
|
||||
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name))
|
||||
Utils.openFile(context, File(event.fileName))
|
||||
}
|
||||
|
||||
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_tomorrow_format">tomorrow (%s)</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="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>
|
||||
@ -431,7 +431,7 @@
|
||||
<string name="hello_blank_fragment">Hello blank fragment</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_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_edit_event">Edit event</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_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_recipients_list_read_format"><![CDATA[<li>&nbsp;%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_unread_format"><![CDATA[<li>&nbsp;%s, read: <font color=red>no</font></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> %s, read: yes</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_date_time_format">%s at %s</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_341" translatable="false">ERROR_VULCAN_API_BAD_REQUEST</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_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_920" translatable="false">EXCEPTION_EDUDZIENNIK_WEB_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>
|
||||
|
||||
@ -304,6 +306,7 @@
|
||||
<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_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_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_920_reason">Wystąpił błąd</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>
|
||||
</resources>
|
||||
|
@ -38,7 +38,7 @@
|
||||
<string name="messages_attachment_format" translatable="false">%s (%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_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="preference_file" translatable="false">pl.szczodrzynski.edziennik_preferences</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_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_recipients_list_read_format"><![CDATA[<li>&nbsp;%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_unread_format"><![CDATA[<li>&nbsp;%s, przeczytano: <font color=red>nie</font></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> %s, przeczytano: tak</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_date_time_format">%s o %s</string>
|
||||
<string name="messages_search">Szukaj</string>
|
||||
@ -1281,4 +1281,7 @@
|
||||
<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_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>
|
||||
|
@ -87,6 +87,7 @@ allprojects {
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url "https://kotlin.bintray.com/kotlinx/" }
|
||||
maven { url "https://dl.bintray.com/wulkanowy/wulkanowy" }
|
||||
maven { url "https://dl.bintray.com/undervoid/PowerPermission" }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user