mobile: Update dependencies and adapt to changes

This commit is contained in:
Elias Projahn 2022-05-07 19:43:55 +02:00
parent b14dcd67f2
commit 8752ac81dd
15 changed files with 326 additions and 826 deletions

View file

@ -26,23 +26,27 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
applicationId "de.johrpan.musicus"
minSdkVersion 21
targetSdkVersion 29
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@ -50,8 +54,6 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
// See https://github.com/ryanheise/audio_service/blob/master/README.md#android-setup
shrinkResources false
}
}
}
@ -62,7 +64,4 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

View file

@ -1,18 +1,24 @@
<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"
package="de.johrpan.musicus">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- TODO: Actually manage obtaining this permission -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:label="Musicus"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
@ -25,13 +31,21 @@
</intent-filter>
</activity>
<service android:name="com.ryanheise.audioservice.AudioService">
<service
android:name="com.ryanheise.audioservice.AudioService"
android:exported="true"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
<receiver
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:exported="true"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>

View file

@ -1,240 +1,12 @@
package de.johrpan.musicus
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract
import androidx.annotation.NonNull
import android.content.Context
import com.ryanheise.audioservice.AudioServicePlugin
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class Document(private val id: String, private val name: String, private val parentId: String?, private val isDirectory: Boolean) {
fun toMap(): Map<String, Any?> {
return mapOf(
"id" to id,
"name" to name,
"parentId" to parentId,
"isDirectory" to isDirectory
)
}
}
class MainActivity : FlutterActivity() {
private val CHANNEL = "de.johrpan.musicus/platform"
private val AODT_REQUEST = 0
private var aodtResult: MethodChannel.Result? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "openTree") {
aodtResult = result
// We will get the result within onActivityResult
openTree()
} else if (call.method == "getChildren") {
val treeUri = Uri.parse(call.argument<String>("treeUri"))
val parentId = call.argument<String>("parentId")
val children = getChildren(treeUri, parentId)
result.success(children.map { it.toMap() })
} else if (call.method == "readFile") {
val treeUri = Uri.parse(call.argument<String>("treeUri"))
val id = call.argument<String>("id")!!
result.success(readFile(treeUri, id))
} else if (call.method == "getUriByName") {
val treeUri = Uri.parse(call.argument<String>("treeUri"))
val parentId = call.argument<String>("parentId")!!
val fileName = call.argument<String>("fileName")!!
result.success(getUriByName(treeUri, parentId, fileName).toString())
} else if (call.method == "readFileByName") {
val treeUri = Uri.parse(call.argument<String>("treeUri"))
val parentId = call.argument<String>("parentId")!!
val fileName = call.argument<String>("fileName")!!
result.success(readFileByName(treeUri, parentId, fileName))
} else if (call.method == "writeFileByName") {
val treeUri = Uri.parse(call.argument<String>("treeUri"))
val parentId = call.argument<String>("parentId")!!
val fileName = call.argument<String>("fileName")!!
val content = call.argument<String>("content")!!
writeFileByName(treeUri, parentId, fileName, content)
result.success(null)
} else {
result.notImplemented()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == AODT_REQUEST) {
if (resultCode == Activity.RESULT_OK && data?.data != null) {
// Drop all old URIs
contentResolver.persistedUriPermissions.forEach {
contentResolver.releasePersistableUriPermission(it.uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
// We already checked for null
val uri = data.data!!
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
aodtResult?.success(uri.toString())
} else {
aodtResult?.success(null)
}
}
}
/**
* Open a document tree using the storage access framework
*
* The result is handled within [onActivityResult]
*/
private fun openTree() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
startActivityForResult(intent, AODT_REQUEST)
}
/**
* List children of a directory
*
* @param treeUri The treeUri from the ACTION_OPEN_DOCUMENT_TREE request
* @param parentId Document ID of the parent directory or null for the top level directory
* @return List of directories and files within the directory
*/
private fun getChildren(treeUri: Uri, parentId: String?): List<Document> {
val realParentId = parentId ?: DocumentsContract.getTreeDocumentId(treeUri)
val children = ArrayList<Document>()
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, realParentId)
val cursor = contentResolver.query(
childrenUri,
arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE),
null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(0)
val name = cursor.getString(1)
val isDirectory = cursor.getString(2) == DocumentsContract.Document.MIME_TYPE_DIR
// Use parentId here to let the consumer know that we are at the top level.
children.add(Document(id, name, parentId, isDirectory))
}
cursor.close()
}
return children
}
/**
* Look for a file by name
*
* @param treeUri The treeUri from the ACTION_OPEN_DOCUMENT_TREE request
* @param parentId The directory in which the file is searched for
* @param fileName Name of the file
* @return The URI of the file or null
*/
private fun getUriByName(treeUri: Uri, parentId: String, fileName: String): Uri? {
var uri: Uri? = null
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, parentId)
val projection = arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME)
// The file system provider doesn't support a select clause.
val cursor = contentResolver.query(childrenUri, projection, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(0)
val name = cursor.getString(1)
if (name == fileName) {
uri = DocumentsContract.buildDocumentUriUsingTree(treeUri, id)
break
}
}
cursor.close()
}
return uri
}
/**
* Read content of a file
*
* @param treeUri The URI from ACTION_OPEN_DOCUMENT_TREE
* @param id The document ID of the file
* @return File content or null
*/
private fun readFile(treeUri: Uri, id: String): String? {
val uri = DocumentsContract.buildDocumentUriUsingTree(treeUri, id)
// TODO: Handle errors.
val input = contentResolver.openInputStream(uri)!!
val result = input.reader().readText()
input.close()
return result
}
/**
* Read content of a file by name
*
* @param treeUri The URI from ACTION_OPEN_DOCUMENT_TREE
* @param parentId Document ID of the parent directory
* @param fileName Name of the file
* @return File content or null
*/
private fun readFileByName(treeUri: Uri, parentId: String, fileName: String): String? {
var uri = getUriByName(treeUri, parentId, fileName)
return if (uri != null) {
// TODO: Handle errors.
val input = contentResolver.openInputStream(uri)!!
val result = input.reader().readText()
input.close()
return result
} else {
null
}
}
/**
* Write to file by name
*
* The file will always have the MIME type application/json.
*
* @param treeUri The URI from ACTION_OPEN_DOCUMENT_TREE
* @param parentId Document ID of the parent directory
* @param fileName Name of the file
* @param content Content to write
* @return File content or null
*/
private fun writeFileByName(treeUri: Uri, parentId: String, fileName: String, content: String) {
var uri = getUriByName(treeUri, parentId, fileName);
if (uri == null) {
val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, parentId)
uri = DocumentsContract.createDocument(contentResolver, parentUri, "application/json", fileName)
}
// TODO: Handle errors.
val output = contentResolver.openOutputStream(uri!!)!!;
val writer = output.writer()
writer.write(content)
writer.close()
output.close()
override fun provideFlutterEngine(context: Context): FlutterEngine {
return AudioServicePlugin.getFlutterEngine(context);
}
}

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/*" />

View file

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -14,7 +14,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}

View file

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View file

@ -1,15 +1,11 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"