Rename top level directories
8
mobile/android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
68
mobile/android/app/build.gradle
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||
if (flutterRoot == null) {
|
||||
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = '1'
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = '1.0'
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.johrpan.musicus"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
7
mobile/android/app/src/debug/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.johrpan.musicus">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
43
mobile/android/app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.johrpan.musicus">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application
|
||||
android:name="io.flutter.app.FlutterApplication"
|
||||
android:label="Musicus"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service android:name="com.ryanheise.audioservice.AudioService">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
BIN
mobile/android/app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -0,0 +1,240 @@
|
|||
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 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()
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 674 B |
BIN
mobile/android/app/src/main/res/drawable-hdpi/ic_pause.png
Normal file
|
After Width: | Height: | Size: 140 B |
BIN
mobile/android/app/src/main/res/drawable-hdpi/ic_play.png
Normal file
|
After Width: | Height: | Size: 272 B |
BIN
mobile/android/app/src/main/res/drawable-hdpi/ic_stop.png
Normal file
|
After Width: | Height: | Size: 102 B |
|
After Width: | Height: | Size: 485 B |
BIN
mobile/android/app/src/main/res/drawable-mdpi/ic_pause.png
Normal file
|
After Width: | Height: | Size: 108 B |
BIN
mobile/android/app/src/main/res/drawable-mdpi/ic_play.png
Normal file
|
After Width: | Height: | Size: 159 B |
BIN
mobile/android/app/src/main/res/drawable-mdpi/ic_stop.png
Normal file
|
After Width: | Height: | Size: 92 B |
|
After Width: | Height: | Size: 1 KiB |
BIN
mobile/android/app/src/main/res/drawable-xhdpi/ic_pause.png
Normal file
|
After Width: | Height: | Size: 162 B |
BIN
mobile/android/app/src/main/res/drawable-xhdpi/ic_play.png
Normal file
|
After Width: | Height: | Size: 288 B |
BIN
mobile/android/app/src/main/res/drawable-xhdpi/ic_stop.png
Normal file
|
After Width: | Height: | Size: 114 B |
|
After Width: | Height: | Size: 1.4 KiB |
BIN
mobile/android/app/src/main/res/drawable-xxhdpi/ic_pause.png
Normal file
|
After Width: | Height: | Size: 202 B |
BIN
mobile/android/app/src/main/res/drawable-xxhdpi/ic_play.png
Normal file
|
After Width: | Height: | Size: 547 B |
BIN
mobile/android/app/src/main/res/drawable-xxhdpi/ic_stop.png
Normal file
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 2.3 KiB |
BIN
mobile/android/app/src/main/res/drawable-xxxhdpi/ic_pause.png
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
mobile/android/app/src/main/res/drawable-xxxhdpi/ic_play.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
mobile/android/app/src/main/res/drawable-xxxhdpi/ic_stop.png
Normal file
|
After Width: | Height: | Size: 244 B |
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:pathData="m45.993,37.589c-1.147,6.587 -2.628,14.34 -4.415,23.149 -1.222,-1.066 -2.952,-1.735 -4.877,-1.735 -3.701,0 -6.701,2.462 -6.701,5.498s3,5.498 6.701,5.498 6.101,-1.669 6.701,-5.498c0.858,-6.02 1.602,-10.357 2.94,-18.602h0.195c2.249,4.725 4.562,9.879 6.941,15.456l2.43,-4.965c-2.647,-5.62 -5.452,-11.86 -8.432,-18.802zM68.7,37.589c-4.441,9.091 -9.305,19.012 -13.563,27.716 0.495,1.106 0.494,1.094 1.631,1.863 3.214,-7.358 6.516,-14.404 9.904,-21.137l0.175,0.022c1.149,8.652 1.97,15.734 2.464,21.246 0.451,-0.087 1.185,-0.131 2.203,-0.131 1.207,0 2.036,0.043 2.486,0.131 -1.629,-9.772 -3.023,-19.675 -4.187,-29.709z"
|
||||
android:strokeWidth=".80618"
|
||||
android:fillColor="#ffc107" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
BIN
mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
|
After Width: | Height: | Size: 2 KiB |
BIN
mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
BIN
mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
BIN
mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
4
mobile/android/app/src/main/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<color name="background">#303030</color>
|
||||
</resources>
|
||||
6
mobile/android/app/src/main/res/values/styles.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">@color/background</item>
|
||||
</style>
|
||||
</resources>
|
||||
7
mobile/android/app/src/profile/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.johrpan.musicus">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
31
mobile/android/build.gradle
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = '../build'
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
4
mobile/android/gradle.properties
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
15
mobile/android/settings.gradle
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||