Added some stuff.

This commit is contained in:
AlbYoda 2025-05-29 23:23:06 +02:00
parent a92f970aaf
commit c5f06a9db9
Signed by untrusted user who does not match committer: AlbYoda
GPG key ID: A539D876452F16FF
7 changed files with 124 additions and 177 deletions

View file

@ -16,10 +16,9 @@ object DriveRequester {
builderModifier: (HttpUrl.Builder) -> Unit, builderModifier: (HttpUrl.Builder) -> Unit,
callback: (String?, Exception?) -> Unit callback: (String?, Exception?) -> Unit
) { ) {
val authPersistence = AuthStatePersistence val authState = AuthStatePersistence.retrieveAuthState(context)
val authState = authPersistence.retrieveAuthState(context)
makeDriveApiCall(context, authState, url, builderModifier, callback) makeDriveApiCall(context, authState, url, builderModifier, callback)
authPersistence.putAuthState(context, authState) AuthStatePersistence.putAuthState(context, authState)
} }
fun populateTree( fun populateTree(

View file

@ -80,7 +80,8 @@ class HomeNotAuthActivity : ComponentActivity() {
.setScopes( .setScopes(
listOf( listOf(
"https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.apps" "https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email",
) )
) )
.build() .build()

View file

@ -13,32 +13,24 @@ You should have received a copy of the GNU General Public License along with DeN
If not, see <https://www.gnu.org/licenses/>. If not, see <https://www.gnu.org/licenses/>.
*/ */
/**
* This file contains the MainActivity activity.
* The activity is responsible for the everyday usage of the app.
* It provides a UI to do all sort of operations
*/
package com.bbc.denadrive package com.bbc.denadrive
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.rememberNavController
import com.bbc.denadrive.apihandlers.DriveResponseBody
import com.bbc.denadrive.apihandlers.MyFile
import com.bbc.denadrive.apihandlers.ResponsesHandler
import com.bbc.denadrive.apihandlers.ResponsesViewer import com.bbc.denadrive.apihandlers.ResponsesViewer
import com.bbc.denadrive.apihandlers.makeDriveApiCall
import com.bbc.denadrive.filehandling.FileDownloader import com.bbc.denadrive.filehandling.FileDownloader
import com.bbc.denadrive.home.ScaffoldWithSidebar import com.bbc.denadrive.home.ScaffoldWithSidebar
import com.bbc.denadrive.oauth.AuthStatePersistence import com.bbc.denadrive.oauth.AuthStatePersistence
import com.bbc.denadrive.ui.theme.DeNaDriveTheme
import net.openid.appauth.AuthorizationException
import net.openid.appauth.AuthorizationResponse
import okhttp3.HttpUrl
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -57,10 +49,7 @@ class MainActivity : ComponentActivity() {
DriveRequester.populateTree(this, responsesViewer.folderTree) DriveRequester.populateTree(this, responsesViewer.folderTree)
// onBackPressedDispatcher.addCallback(this) { Log.e("vediamo", AuthStatePersistence.retrieveAuthState(this).jsonSerializeString())
// if (currentPath != "root") goToFolder("", false)
// else finish()
// }
onBackPressedDispatcher.addCallback(this) { onBackPressedDispatcher.addCallback(this) {
if (responsesViewer.currentFolderTree.value.parent != null) { if (responsesViewer.currentFolderTree.value.parent != null) {
@ -72,7 +61,6 @@ class MainActivity : ComponentActivity() {
} }
val fileDownloader = FileDownloader(this) val fileDownloader = FileDownloader(this)
// goToFolder(parentFolder = "root", forward = true, isFirstRequest = true)
setContent { setContent {
// val navController = rememberNavController() // val navController = rememberNavController()
@ -84,10 +72,11 @@ class MainActivity : ComponentActivity() {
"You pressed the file ${file.name}", "You pressed the file ${file.name}",
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
if (file.mimeType.substringAfterLast(".") in exportSupportedFiles) { /*if (file.mimeType.substringAfterLast(".") in exportSupportedFiles) {
Log.e("MYTAG", "Supported!", )
fileDownloader.downloadFile( fileDownloader.downloadFile(
"https://www.googleapis.com/drive/v3/files/${file.id}/export", file,
"application/pdf" exported = true
) )
} else { } else {
Toast.makeText( Toast.makeText(
@ -95,109 +84,31 @@ class MainActivity : ComponentActivity() {
"This file has no default action. Download support coming soon", "This file has no default action. Download support coming soon",
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }*/
fileDownloader.downloadFile(
file,
exported = file.mimeType.substringAfterLast(".") in exportSupportedFiles
)
}, },
{ folder -> { folder ->
responsesViewer.currentFolderTree.value = responsesViewer.currentFolderTree.value =
responsesViewer.currentFolderTree.value.stepForward(folder.id) responsesViewer.currentFolderTree.value.stepForward(folder.id)
// responsesViewer.currentFolderTree.value =
// responsesViewer.currentFolderTree.value.goToPath("$folder/")
// parent ->
// goToFolder(parent, true)
} }
) { ) {
responsesViewer.toggleUpdaters() responsesViewer.toggleUpdaters()
// getApps() DriveRequester.makeNewRequest(
} this,
} "https://people.googleapis.com/v1/people/me?personFields=emailAddresses",
} {}) { resp, ex ->
if (ex != null) {
Log.e("noGood", "$ex")
private fun getApps() {
val myUrl = "https://www.googleapis.com/drive/v3/apps"
DriveRequester.makeNewRequest(this, myUrl, { }) {
response, exception ->
if (exception != null) {
Log.e("notGood", "getApps: $exception", )
} else { } else {
Log.e("GOOD", "getApps: $response", ) Log.e("good", "$resp")
}
}
} }
} }
} }
// private fun askForFolder(folderId: String) {
// val myUrl = "https://www.googleapis.com/drive/v3/files"
// makeNewRequest(myUrl, { builder ->
// builder.addQueryParameter("q", "'$folderId' in parents")
// }) { response, exception ->
// if (exception != null) {
// Log.e("TAG", "askForFolder: Something went wrong")
// } else {
// // fai quel che ti pare
// }
// }
// }
//
// private fun goToFolder(
// parentFolder: String, forward: Boolean, isFirstRequest: Boolean = false
// ) {
// responsesViewer.lastResponseBody.value = null
// val actualFolder: String
// if (!isFirstRequest) {
// if (forward) {
// currentPath += "/$parentFolder"
// actualFolder = parentFolder
// } else {
// currentPath = currentPath.substringBeforeLast("/")
// actualFolder =
// if (currentPath != "root") currentPath.substringAfterLast("/")
// else "root"
// }
// } else actualFolder = parentFolder
//
// val myUrl = "https://www.googleapis.com/drive/v3/files?trashed=false&orderBy=folder"
// makeNewRequest(myUrl, { builder ->
// builder.addQueryParameter("q", "'$actualFolder' in parents")
// }) { response, exception ->
// if (exception != null) {
// Log.e("ECCOLO", "${exception.cause}")
// Log.e("ECCOLO", "${exception.message}")
// } else {
// Log.e("UOOOO", "$response")
// responsesViewer.newResponseArrived(response)
// }
// }
// }
//
// private fun makeNewRequest(
// url: String,
// builderModifier: (HttpUrl.Builder) -> Unit,
// callback: (String?, Exception?) -> Unit
// ) {
// val authPersistence = AuthStatePersistence
// val authState = authPersistence.retrieveAuthState(this)
// makeDriveApiCall(this, authState, url, builderModifier, callback)
// authPersistence.putAuthState(this, authState)
// }
// override fun onNewIntent(intent: Intent) {
// super.onNewIntent(intent)
// val resp = AuthorizationResponse.fromIntent(intent)
// val ex = AuthorizationException.fromIntent(intent)
// if (resp != null) {
// Toast.makeText(this, "YES", Toast.LENGTH_SHORT).show()
// } else {
// if (ex != null) {
// Toast.makeText(this, "NOPE ${ex.error}", Toast.LENGTH_SHORT).show()
// }
// }
//
// setContent {
//// val navController = rememberNavController()
//// ScaffoldWithSidebar(navController) { }
// Greeting("bah")
// }
// }
} }
//@Composable //@Composable

View file

@ -23,7 +23,7 @@ import com.bbc.denadrive.FolderTree
class ResponsesViewer: ViewModel() { class ResponsesViewer: ViewModel() {
val rootFolder: MyFile = MyFile( private val rootFolder: MyFile = MyFile(
kind = "drive#file", kind = "drive#file",
mimeType = "application/vnd.google-apps.folder", mimeType = "application/vnd.google-apps.folder",
id = "root", id = "root",
@ -42,31 +42,26 @@ class ResponsesViewer: ViewModel() {
updater2.value = !updater2.value updater2.value = !updater2.value
} }
// private var lastResponse: String = "" /*private var lastResponse: String = ""
// private var isComplete: Boolean = true private var isComplete: Boolean = true
// private lateinit var respBuilder: StringBuilder private lateinit var respBuilder: StringBuilder
fun newResponseArrived(response: String?) {
if (response == null) return private fun checkForCompleteness(): Pair<Boolean, DriveResponseBody?> {
lastResponseBody.value = ResponsesHandler.newResponseArrived<DriveResponseBody>(response) return try {
Pair(true, Json.decodeFromString<DriveResponseBody>(respBuilder.toString()))
} catch (e: SerializationException) {
Pair(false, null)
}
} }
private fun updateCompleteness() {
// private fun checkForCompleteness(): Pair<Boolean, DriveResponseBody?> { val pair = checkForCompleteness()
// return try { isComplete = pair.first
// Pair(true, Json.decodeFromString<DriveResponseBody>(respBuilder.toString())) if (isComplete) {
// } catch (e: SerializationException) { lastResponse = respBuilder.toString()
// Pair(false, null) lastResponseBody.value = pair.second
// } }
// } }*/
// private fun updateCompleteness() {
// val pair = checkForCompleteness()
// isComplete = pair.first
// if (isComplete) {
// lastResponse = respBuilder.toString()
// lastResponseBody.value = pair.second
// }
// }
} }

View file

@ -5,6 +5,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.bbc.denadrive.apihandlers.MyFile
import com.bbc.denadrive.oauth.AuthStatePersistence import com.bbc.denadrive.oauth.AuthStatePersistence
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -18,40 +19,33 @@ import java.net.URL
class FileDownloader(private val context: Context) { class FileDownloader(private val context: Context) {
fun downloadFile(fileUrl: String, mimeType: String) { fun downloadFile(file: MyFile, exported: Boolean) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val tempFile = downloadIt(fileUrl, mimeType) val tempFile = downloadIt(file, exported)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
tempFile?.let { tempFile?.let {
openFile(it) openFile(it, exported)
} }
} }
} }
} }
private fun downloadIt(fileUrl: String, mimeType: String): File? { private fun downloadIt(file: MyFile, exported: Boolean): File? {
var tempFile: File? = null var tempFile: File? = null
val authState = AuthStatePersistence.retrieveAuthState(context) val authState = AuthStatePersistence.retrieveAuthState(context)
val query = if (exported) "export?mimeType=application/pdf" else "download"
val fileUrl = "https://www.googleapis.com/drive/v3/files/${file.id}/$query"
Log.e("SEE", fileUrl, )
try { try {
tempFile = File.createTempFile("tempFile", ".pdf", context.cacheDir) tempFile =
// val builder = fileUrl.toHttpUrl().newBuilder() File.createTempFile(file.name, if (exported) ".pdf" else "", context.cacheDir)
// builder.addQueryParameter("mimeType", mimeType) val urlConnection = URL(fileUrl).openConnection() as HttpURLConnection
// val urlConnection = builder.build().toUrl().openConnection() as HttpURLConnection
val urlConnection = URL("$fileUrl?mimeType=application/pdf").openConnection() as HttpURLConnection
urlConnection.setRequestProperty("Authorization", "Bearer ${authState.accessToken}") urlConnection.setRequestProperty("Authorization", "Bearer ${authState.accessToken}")
urlConnection.connect() urlConnection.connect()
// val inputStream1 = urlConnection.inputStream
// val buffer1 = ByteArray(4)
// inputStream1.read(buffer1)
// val header = String(buffer1)
// if (!header.startsWith("%PDF")) {
// Log.e("FileDownloader", "Downloaded file is not a valid PDF")
// }
if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) { if (urlConnection.responseCode == HttpURLConnection.HTTP_OK) {
Log.e("MA", "downloadIt: andato", )
val inputStream = urlConnection.inputStream val inputStream = urlConnection.inputStream
val outputStream = FileOutputStream(tempFile) val outputStream = FileOutputStream(tempFile)
@ -65,7 +59,10 @@ class FileDownloader(private val context: Context) {
inputStream.close() inputStream.close()
urlConnection.disconnect() urlConnection.disconnect()
} else { } else {
Log.e("FileDownloader", "Error: ${urlConnection.responseCode} - ${urlConnection.responseMessage}") Log.e(
"FileDownloader",
"Error: ${urlConnection.responseCode} - ${urlConnection.responseMessage}"
)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -76,10 +73,12 @@ class FileDownloader(private val context: Context) {
} }
private fun openFile(file: File) { private fun openFile(file: File, exported: Boolean) {
val uri: Uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) val uri: Uri =
FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
val intent = Intent(Intent.ACTION_VIEW).apply { val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "application/pdf") if (exported) setDataAndType(uri, "application/pdf")
else setData(uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
} }
context.startActivity(intent) context.startActivity(intent)

View file

@ -41,6 +41,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@ -182,7 +184,7 @@ fun MyListVisualizer(
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
LazyColumn { LazyColumn {
items(fileList) { file -> items(fileList) { file ->
val logo = painterResource(id = getId(file.mimeType)) val logo = painterResource(id = getLogoId(file.mimeType))
val isFolder = file.mimeType.endsWith("folder") val isFolder = file.mimeType.endsWith("folder")
val onClick = if (isFolder) onFolderClick val onClick = if (isFolder) onFolderClick
else onFileClick else onFileClick
@ -252,7 +254,7 @@ fun AnotherVisualizer(
val updater1 = responsesViewer.updater1.value val updater1 = responsesViewer.updater1.value
val updater2 = responsesViewer.updater2.value val updater2 = responsesViewer.updater2.value
if (currentFolderTree.isLeaf() && (updater1 || updater2)) { if (currentFolderTree.isLeaf() && (updater1 || updater2)) {
Log.e("Visualizer", "AnotherVisualizer: ${currentFolderTree.value}", ) Log.e("Visualizer", "AnotherVisualizer: ${currentFolderTree.value}")
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(text = "How did you get here? You're inside a file...") Text(text = "How did you get here? You're inside a file...")
} }
@ -263,29 +265,49 @@ fun AnotherVisualizer(
} else if (currentFolderTree.getChildren().isEmpty()) { } else if (currentFolderTree.getChildren().isEmpty()) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Empty folder") Text("Empty folder")
// Loading()
} }
} else { } else {
val state = rememberLazyListState()
val fileList = currentFolderTree.getChildren().map { tree -> tree.value } val fileList = currentFolderTree.getChildren().map { tree -> tree.value }
// Log.e("Visualizer", "AnotherVisualizer: ${currentFolderTree.value}", )
// Log.e("Visualizer", "AnotherVisualizer: ${currentFolderTree.getChildren()}", )
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
LazyColumn { LazyColumn (
items(fileList) { file -> state = state
val logo = painterResource(id = getId(file.mimeType)) ) {
itemsIndexed(fileList) { index, file ->
val logo = painterResource(id = getLogoId(file.mimeType))
val isFolder = file.mimeType.endsWith("folder") val isFolder = file.mimeType.endsWith("folder")
val onClick = if (isFolder) onFolderClick val onClick = if (isFolder) onFolderClick
else onFileClick else onFileClick
LogoTextBox(logo, file.name) {
var mod = Modifier
.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.onBackground)
// .padding(horizontal = 1.dp)
mod = if (index == 0) {
mod.padding(vertical = 1.dp)
} else {
mod.padding(bottom = 1.dp)
}
mod = mod
.background(color = MaterialTheme.colorScheme.background)
.clickable {
onClick(file) onClick(file)
} }
FileVisualizer(
logo,
file.name,
mod
)
} }
} }
} }
} }
} }
fun getId(mimeType: String): Int { fun getLogoId(mimeType: String): Int {
val extracted = mimeType.substringAfterLast(".") val extracted = mimeType.substringAfterLast(".")
return when (extracted) { return when (extracted) {
"document" -> R.drawable.docslogo_ok "document" -> R.drawable.docslogo_ok
@ -295,6 +317,24 @@ fun getId(mimeType: String): Int {
} }
} }
@Composable
fun FileVisualizer(
logoPainter: Painter,
text: String,
modifier: Modifier = Modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
) {
Image(
painter = logoPainter,
contentDescription = "Item logo"
)
Text(text = text, style = MaterialTheme.typography.titleLarge)
}
}
@Composable @Composable
fun LogoTextBox( fun LogoTextBox(
logoPainter: Painter, logoPainter: Painter,
@ -335,3 +375,4 @@ fun LogoTextBox(
} }
} }
} }

View file

@ -66,7 +66,7 @@ class LoadingActivity : ComponentActivity() {
setContent { setContent {
DeNaDriveTheme { DeNaDriveTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
LoadingScreen(modifier = Modifier.padding(innerPadding)) LoadingScreen(text = "Loading...", modifier = Modifier.padding(innerPadding))
} }
} }
} }
@ -106,6 +106,7 @@ class LoadingActivity : ComponentActivity() {
@Composable @Composable
fun LoadingScreen( fun LoadingScreen(
text: String,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// State to control the rotation animation // State to control the rotation animation