Cross compile platforms - android

I wanted to create sbt cross platforms (Hello world app ) including JVM , Scala Native , Android and scalajs , I have succeed in compiling all of them except the android and this is my build.sbt:
name := "Cross-Platforms-ScalaNative-JVM"
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
val sharedSettings = Seq(scalaVersion := "2.11.12")
lazy val bar =
// select supported platforms
crossProject(JSPlatform, JVMPlatform, NativePlatform)
.crossType(CrossType.Full)
.settings(sharedSettings)
.jsSettings(
libraryDependencies += "org.querki" %%% "jquery-facade" % "1.2")
.jvmSettings(/* ... */)
.nativeSettings(/* ... */)
lazy val barJS = bar.js
lazy val barJVM = bar.jvm
lazy val barNative = bar.native
My question how I can create an android platform in this project; what changes should be to my SBT?

Android build is basically an JVM build with an extra steps - taking JVM bytecode, optionally running proguard to minimize code and compiling it into Android format.
Here you have a lot of information about how to setup normal android build in sbt: https://scala-android.org/quickstart/.
The difference would be by applying androidBuild (and all Android-related settings) only to JVM projects:
val myProjectJVM = myProject.jvm
.settings(androidBuild: _*)
However, considering that Android build and normal Scala build will look differently (as normal JVM application has architecturally little to do with Android apps - e.g. Main.main(Array[String]) vs Android fragments and services) it would be a better idea to have a module for some shared JVM code and a dedicated projects for the platform-specific parts:
val commonJVM = myProject.jvm
val myProjectJVM = project.in(file("java-specific"))
.dependsOn(commonJVM)
val myProjectAndroid = project.in(file("android-specific"))
.settings(androidBuild)
.dependsOn(commonJVM)
That architectural difference is most likely a reason why nobody even considers making Android a fourth option in a cross project - you have a different runtime dependencies, a different way to start the application, so you would have more parts of the code that differ than in common. Submodules with code dedicated to platform depending on common dependency (even cross-compiled one) is a better idea.

Related

How to determine build type in kotlin-multiplatform project

I’m working on a multiplaform project, iOS and JVM (I’m not targeting Android directly). Depending on the build type (debug or release) I want to configure the logging level (i.e. to print only errors in release). Since there is no a BuildConfig class available, how can I know from commonMain the build type?
Not a direct answer to the question, but for android/ios one can define a property like this:
in commonMain:
expect val isDebug: Boolean
in androidMain:
actual val isDebug = BuildConfig.DEBUG
in iosMain:
actual val isDebug = Platform.isDebugBinary
It is not possible right now for all possible targets. Build type is resolved only for android.
But you can create two versions of the module e.g. module and module-debug.
Then declare in module:
const val IS_DEBUG = false
in module-debug:
const val IS_DEBUG = true
Finally in the resulting application or module gradle configuration you can declare dependency on what you need. E.g.:
if (DEBUG_ENV) // you need to set DEBUG_ENV from property or environment variable
implementation(project(":module-debug"))
else
implementation(project(":module"))
or for android:
debugImplementation(project(":module-debug"))
releaseImplementation(project(":module"))
This way you can change logic using IS_DEBUG constant for every target or can create even completely different implementations of something in debug and release modules.

refer aosp functions in Android Core App using gradle

How to refer aosp hidden methods in core android app using gradle build system. I am referring framework and other jars from out folder but unable to access hidden API. Is there any way to access hidden methods.
There are a couple ways.
Android Hidden API
As long as you're fine targeting API 27 (there's no API 28 release as of writing this) this way works great. You can directly call the hidden methods with proper code completion and everything.
Note: As of writing this, you'll need to set your Gradle classpath to 3.1.4. 3.2.0 adds some sort of integrity check that breaks builds when using a modified framework JAR.
Use reflection
It's not ideal, but it'll work if you want to target API 28, or you want to use Gradle 3.2.0.
Example (Kotlin):
val IWindowManager = Class.forName("android.view.IWindowManager")
val IWindowManagerStub = Class.forName("android.view.IWindowManager\$Stub")
val ServiceManager = Class.forName("android.os.ServiceManager")
val binder = ServiceManager.getMethod("checkService", String::class.java).invoke(null, Context.WINDOW_SERVICE)
val iWindowManagerInstance = IWindowManagerStub.getMethod("asInterface", Binder::class.java).invoke(null, binder)
val hasNavBar = IWindowManager.getMethod("hasNavigationBar").invoke(iWindowManagerInstance) as Boolean

Android h3: A Hexagonal Hierarchical Geospatial Indexing System

I want to integrate h3 java binding library to my android app and I'm getting following exception:
java.lang.UnsatisfiedLinkError: No native resource found at /android-armv7l/libh3-java.so
at com.uber.h3core.H3CoreLoader.copyResource(H3CoreLoader.java:67)
Does anyone used this library before for Android OS?
Thank you.
Initially, following the intended usage as seen in their README should make it work. If it doesn't, see below.
Known Issue: Android, can't use library
UnsatisfiedLinkError: This can be encountered when the corresponding native library is not copied/detected in the project. Following NickRadu's workaround should make it work.
Below is a step-by-step guide.
Add a JNI folder in your project app folder and rename it jniLibs (app/src/main/jniLibs) (for some reason, having it named jni only doesn't work for me).
Get the H3 JAR (make sure you use the same version) and extract the JAR contents.
Copy the folders prefixed with android- and insert them in the jniLibs folder (from step 1).
Rename the copied folders, remove the android- prefix.
Add splits { abi { enable false } } to your app's build.gradle file (within android).
Done. In general, the library should now work as expected.
If during the app installation you encounter:
INSTALL_FAILED_NO_MATCHING_ABIS,
then depending on your test device, create a copy of the folder (along with its contents) and rename it as needed.
For example, a device running on arm64-v8a, I just made a copy of the arm64 folder and renamed it to arm64-v8a. Or if you're using an emulator, make sure that you're not using one with an x86 CPU.
D8 errors: Invoke-customs are only supported starting with Android O (--min-api 26), add these compile options in your app's build.gradle (within android -- note that it may change depending on your system's Java version)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
Note: It is best to test the app on multiple CPU architecture types first to see it's behavior.
To quickly see the CPU Architecture of the device, you could install Droid Hardware Info, or run a quick test code yourself.
Here's a test block I used and its corresponding result logs:
private fun testH3() {
val h3 = H3Core.newSystemInstance()
val lat = 37.775938728915946
val lng = -122.41795063018799
val resolution = 8
val hexAddr = h3.geoToH3Address(lat, lng, resolution)
val hex = h3.stringToH3(hexAddr)
val kRingsResult = h3.kRings(hexAddr, 1)
Log.d("H3Test", "geoToH3Address: $hexAddr")
Log.d("H3Test", "stringToH3: $hex")
Log.d("H3Test", "kRings: $kRingsResult")
}
Result:
D/H3Test: geoToH3Address: 8828308281fffff
D/H3Test: stringToH3: 613196570331971583
D/H3Test: kRings: [[8828308281fffff], [8828308281fffff, 882830828dfffff, 8828308285fffff, 8828308287fffff, 8828308283fffff, 882830828bfffff, 8828308289fffff]]
I made a sample project where the library works as expected. See android-uber-h3-sample.
Also be advised that the library will not work on Android api < 26 without some modifications to the code. The function that H3Core relies on to parse the hex long to hex string Long.parseUnsignedInt was not added to Android Java until api 26.

Scala reflection java.rmi dependency, can it work on Android?

I would like to use the Scala (2.11) reflection package's runtime mirror in a Scala application compiled for android which is being build using Scala on android.
I was able to fiddle with ProGuard options in order to make it include the required Scala classes. However when I try to get a mirror instance:
universe.runtimeMirror(this.getClass.getClassLoader)
(Indeed it fails during the lazy computation of universe)
The application crashes in run time:
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/rmi/Remote;
at scala.reflect.internal.Definitions$DefinitionsClass.RemoteInterfaceClass$lzycompute(Definitions.scala:370)
at scala.reflect.internal.Definitions$DefinitionsClass.RemoteInterfaceClass(D efinitions.scala:370)
at scala.reflect.runtime.JavaUniverseForce$class.force(JavaUniverseForce.scal a:255)
at scala.reflect.runtime.JavaUniverse.force(JavaUniverse.scala:16)
at scala.reflect.runtime.JavaUniverse.init(JavaUniverse.scala:147)
at scala.reflect.runtime.JavaUniverse.<init>(JavaUniverse.scala:78)
at scala.reflect.runtime.package$.universe$lzycompute(package.scala:17)
at scala.reflect.runtime.package$.universe(package.scala:17)
This crash is for me as expected as it isn't:
It is expected as java.rmi is not part of the Android API and I should expect any code trying to load its classes to crash.
It is unexpected as I didn't know that Scala's reflect package used java.rmi
I have traced the code to were rmi is required, that is to JavaUniverse (a trait mixed in JavaUniverse class) force method:
...
definitions.RemoteInterfaceClass
...
Which leads to DefinitionsClass:
lazy val RemoteInterfaceClass = requiredClass[java.rmi.Remote]
Am I wrong to think that this is a no-go for Scala reflection in Android?
If I am, what could be a workaround to this problem?
To summarize your solution and a related solution, it is sufficient to add two files, and modify build.sbt to include:
dexAdditionalParams in Android += "--core-library"
Add java/rmi/Remote.java to your project with the content:
package java.rmi;
public interface Remote {}
Add java/rmi/RemoteException.java to your project with the content:
package java.rmi;
public interface RemoteException {}

Runtime error using Xamarin Android with F# and ReactiveUI

I have created a Xamarin Android project that is using F# and ReactiveUI.
When loading my Dashboard, I encounter a runtime exception (of type MissingMethodException) on the inherit line of this code snippet:
type DashboardViewModel(?host: IScreen) =
inherit ReactiveViewModel()
let host = LocateIfNone host
member __.Title with get() = "Dashboard"
interface IRoutableViewModel with
member __.HostScreen = host
member __.UrlPathSegment = "Dashboard"
The error message reads
Method 'Microsoft.FSharp.Quotations.FSharpExpr.Deserialize40' not found.
The ReactiveViewModel type is a thin wrapper around ReactiveObject:
type ReactiveViewModel() as this =
inherit ReactiveObject()
let mutable message = noMessage
let uiContext = SynchronizationContext.Current
member __.SyncContext with get() = uiContext
member this.Message
with get() = message
and set(value) =
this.RaiseAndSetIfChanged(&message, value, "Message") |> ignore
if message <> noMessage then this.RaiseAndSetIfChanged(&message, noMessage, "Message") |> ignore
member val MessageSent =
this.WhenAnyValue(toLinq <# fun vm -> vm.Message #>).ObserveOn(RxApp.MainThreadScheduler).Where(fun m -> m <> noMessage) with get
The project is open source: at the moment, it contains very little content. It can be found at https://github.com/SpiegelSoft/Astrid.
I have submitted a bug on the Xamarin bug tracker: https://bugzilla.xamarin.com/show_bug.cgi?id=51000
Are there any known fixes I can implement myself, so that I can close the bug of my own accord?
UPDATE 1
I've been investigating this issue this weekend.
The FSharp.Core version that is loaded is stuck on the obsolete version 2.3.98.1. This corresponds to the FSharp.Core.dll file in
C:\Program Files (x86)\Reference
Assemblies\Microsoft\Framework\MonoAndroid\v1.0
I have tried to remove this version and load the NuGet package FSharp.Core; however, when I build the Android project, the path always reverts to the obsolete file in the Reference Assemblies path.
Is there a way to override this behaviour?
UPDATE 2
Replacing the FSharp.Core.dll file in the Reference Assemblies path fixes the issue, but this is a very unsatisfactory sticking plaster, which I can't ask my users to apply. Ideally, I would like to find a way to prevent the .Droid project from loading FSharp.Core from the GAC rather than the NuGet package.
I just ran into a very similar issue the other day. My android app had a reference to a Profile7 F# PCL library, which made use of List.unfold, which I believe was introduced in F# 4. When I used the library in my app, I saw a MissingMethodException similar to what you are seeing. The version of FSharp.Core that Xamarin references by default when creating a new Android app didn't have this newer method. I got around it by editing the .fsproj file for the app to remove the original reference to FSharp.Core, and replaced it with a reference to the newer version ( I copy/pasted the tag from the PCL .fsproj file). It looks something like this:
<Reference Include="FSharp.Core">
<Name>FSharp.Core</Name>
<Private>True</Private>
<AssemblyName>FSharp.Core.dll</AssemblyName>
<HintPath>$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\.NETCore\$(TargetFSharpCoreVersion)\FSharp.Core.dll</HintPath>
</Reference>
I was suprised to find that this seems to have fixed the problem for me. I'm not sure if I'll run into other issues down the line, but it may be worth trying this if you havent already.
UPDATE If this doesn't work immediately, follow the sequence of steps in Rob Lyndon's answer.
It appears that this has been fixed by the GitHub commit
https://github.com/xamarin/xamarin-android/commit/df41af046000556ed82f638e8041b7f718966a92
which removes FSharp.Core from the list of framework assemblies, and allows the project to be built without the NuGet FSharp.Core assembly being replaced.
Until this fix is released into the SDK, there is a workaround. The answer submitted by user3850711 works, but you need to apply the changes in a specific sequence, because otherwise the reference will be overwritten during the build.
Delete the existing reference to FSharp.Core.
Install or reinstall the FSharp.Core NuGet package.
Unload the project and add <HintPath>packages\FSharp.Core.4.0.0.1\lib\portable-net45+monoandroid10+monotouch10+xamarinios10\FSharp.Core.dll</HintPath> to the FSharp.Core project reference.

Categories

Resources