Add DSBDownloader
Add update DSBActivity MainContent
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package com.schoolapp.cleverclass
|
package com.schoolapp.cleverclass
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -19,12 +25,24 @@ import androidx.compose.material3.TopAppBar
|
|||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class DSBActivity : ComponentActivity() {
|
class DSBActivity : ComponentActivity() {
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
downloadDSB(this@DSBActivity)
|
||||||
|
}
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
CleverClassTheme {
|
CleverClassTheme {
|
||||||
Surface(
|
Surface(
|
||||||
@@ -42,7 +60,7 @@ class DSBActivity : ComponentActivity() {
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DSBContent(activity: ComponentActivity){
|
fun DSBContent(activity: ComponentActivity){
|
||||||
Column() {
|
Column {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||||
title = {
|
title = {
|
||||||
@@ -63,8 +81,27 @@ fun DSBContent(activity: ComponentActivity){
|
|||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
|
||||||
Column() {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
|
val mainFolder = File(activity.filesDir, "dataDSB")
|
||||||
|
|
||||||
|
mainFolder.listFiles()?.forEach { folder ->
|
||||||
|
DayPrefab(folder = folder)
|
||||||
|
Spacer(modifier = Modifier.height(48.dp))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DayPrefab(folder: File){
|
||||||
|
val imageFiles = folder.listFiles { file -> file.extension == "jpg" }?.toList()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
imageFiles?.forEach {
|
||||||
|
val imageBitmap = BitmapFactory.decodeFile(it.absolutePath)
|
||||||
|
|
||||||
|
Image(bitmap = imageBitmap.asImageBitmap(), contentDescription = null)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
179
app/src/main/java/com/schoolapp/cleverclass/DSBDownloader.kt
Normal file
179
app/src/main/java/com/schoolapp/cleverclass/DSBDownloader.kt
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
package com.schoolapp.cleverclass
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Base64
|
||||||
|
import android.util.Log
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.net.HttpURLConnection
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
import java.util.zip.GZIPOutputStream
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun downloadDSB(context: Context){
|
||||||
|
val mainDir = createDataFolder(context)
|
||||||
|
|
||||||
|
val jsonResponse = downloadDataTask()
|
||||||
|
|
||||||
|
downloadImagesTask(jsonResponse, mainDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadDataTask() : String {
|
||||||
|
return try {
|
||||||
|
val requestBody = createRequestBody()
|
||||||
|
val jsonRequest = createJsonRequest(requestBody)
|
||||||
|
|
||||||
|
val responseData = fetchData(jsonRequest)
|
||||||
|
|
||||||
|
responseData
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("HTTP", "Error: ${e.message}")
|
||||||
|
"-1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRequestBody(): String {
|
||||||
|
val username = "149002"
|
||||||
|
val password = "Vertretungsplan"
|
||||||
|
val currentDateTime = LocalDateTime.now().toString()
|
||||||
|
|
||||||
|
val requestBody = JSONObject().apply {
|
||||||
|
put("UserId", username)
|
||||||
|
put("UserPw", password)
|
||||||
|
put("AppVersion", "2.5.9")
|
||||||
|
put("Language", "de")
|
||||||
|
put("OsVersion", "28 8.0")
|
||||||
|
put("AppId", UUID.randomUUID().toString())
|
||||||
|
put("Device", "SM-G930F")
|
||||||
|
put("BundleId", "de.heinekingmedia.dsbmobile")
|
||||||
|
put("Date", currentDateTime)
|
||||||
|
put("LastUpdate", currentDateTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Base64.encodeToString(GZIPCompress(requestBody.toString().toByteArray()), Base64.DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createJsonRequest(requestBody: String): JSONObject {
|
||||||
|
return JSONObject().apply {
|
||||||
|
put("req", JSONObject().apply {
|
||||||
|
put("Data", requestBody)
|
||||||
|
put("DataType", 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchData(jsonRequest: JSONObject): String {
|
||||||
|
val url = URL("https://app.dsbcontrol.de/JsonHandler.ashx/GetData")
|
||||||
|
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||||
|
requestMethod = "POST"
|
||||||
|
setRequestProperty("Content-Type", "application/json")
|
||||||
|
doOutput = true
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.outputStream.use { outputStream ->
|
||||||
|
outputStream.write(jsonRequest.toString().toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (connection.responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
|
val responseData = connection.inputStream.bufferedReader().use { it.readText() }
|
||||||
|
val compressedResponseData = JSONObject(responseData).getString("d")
|
||||||
|
decompressGZIPAndDecodeBase64(compressedResponseData)
|
||||||
|
} else {
|
||||||
|
Log.e("HTTP", "Error response code: ${connection.responseCode}")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun GZIPCompress(data: ByteArray): ByteArray {
|
||||||
|
return ByteArrayOutputStream().use { outputStream ->
|
||||||
|
GZIPOutputStream(outputStream).bufferedWriter().use { it.write(String(data)) }
|
||||||
|
outputStream.toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decompressGZIPAndDecodeBase64(compressedData: String): String {
|
||||||
|
val compressedByteArray = Base64.decode(compressedData, Base64.DEFAULT)
|
||||||
|
val inputStream = ByteArrayInputStream(compressedByteArray)
|
||||||
|
val outputStream = ByteArrayOutputStream()
|
||||||
|
GZIPInputStream(inputStream).use { gzipInputStream ->
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var len: Int
|
||||||
|
while (gzipInputStream.read(buffer).also { len = it } > 0) {
|
||||||
|
outputStream.write(buffer, 0, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputStream.toString("UTF-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadImagesTask(jsonResponse: String, mainDir : File){
|
||||||
|
clearFolder(mainDir)
|
||||||
|
|
||||||
|
val jsonResponseObject = JSONObject(jsonResponse)
|
||||||
|
val jsonDaysObject = jsonResponseObject.getJSONArray("ResultMenuItems").getJSONObject(0).getJSONArray("Childs").getJSONObject(0).getJSONObject("Root").getJSONArray("Childs")
|
||||||
|
|
||||||
|
for (d in 0 until jsonDaysObject.length()) {
|
||||||
|
val jsonDayObject = jsonDaysObject.getJSONObject(d)
|
||||||
|
val dayID = jsonDayObject.get("Id")
|
||||||
|
|
||||||
|
val day = File(mainDir, dayID.toString())
|
||||||
|
day.mkdir()
|
||||||
|
|
||||||
|
val jsonPagesObject = jsonDayObject.getJSONArray("Childs")
|
||||||
|
|
||||||
|
for (p in 0 until jsonPagesObject.length()) {
|
||||||
|
val jsonPageObject = jsonPagesObject.getJSONObject(p)
|
||||||
|
val pageIndex = jsonPageObject.get("Index")
|
||||||
|
val imageLink = jsonPageObject.get("Detail").toString()
|
||||||
|
|
||||||
|
downloadImage(day, URL(imageLink), "$pageIndex.jpg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun downloadImage(folder: File, url: URL, fileName: String){
|
||||||
|
val file = File(folder, fileName)
|
||||||
|
|
||||||
|
val connection = url.openConnection()
|
||||||
|
val inputStream = connection.getInputStream()
|
||||||
|
val outputStream = FileOutputStream(file)
|
||||||
|
|
||||||
|
val buffer = ByteArray(1024)
|
||||||
|
var bytesRead: Int
|
||||||
|
|
||||||
|
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream.close()
|
||||||
|
outputStream.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDataFolder(context: Context) : File {
|
||||||
|
val folderName = "dataDSB"
|
||||||
|
val folder = File(context.filesDir, folderName)
|
||||||
|
|
||||||
|
if (!folder.exists()) {
|
||||||
|
folder.mkdir()
|
||||||
|
}
|
||||||
|
return folder
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearFolder(directory: File) {
|
||||||
|
if (directory.exists()) {
|
||||||
|
val files = directory.listFiles()
|
||||||
|
if (files != null) {
|
||||||
|
for (file in files) {
|
||||||
|
if (file.isDirectory) {
|
||||||
|
clearFolder(file)
|
||||||
|
}
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user