I have an app I created using Flutter / Dart / VSCode / Android Studio and wanted to work on it with Visual Studio App Center.
I add an Android app using Java / Kotlin, then add
dependencies { def appCenterSdkVersion = '3.2.2' implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}" implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}" }
to app/build.gradle but that's about as far as I get before messing up.
I don't know what 'my app's main class activity' is, I'm guessing it's not main.dart as when I put the following code in there I get errors.
import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.analytics.Analytics;
import com.microsoft.appcenter.crashes.Crashes;
I also can't find onCreate anywhere (other than AndroidManifest.xml where it's commented out).
I've looked through Getting started with the Android SDK, but can't figure it out.
Can anyone help me out?
I'm pretty much at the end of my tether so any help would be greatly appreciated!
UPDATE
build.gradle
dependencies {
def appCenterSdkVersion = '3.2.2'
implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}"
implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
}
MainActivity.kt
import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.analytics.Analytics;
import com.microsoft.appcenter.crashes.Crashes;
AndroidManifest.xml
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
AppCenter.start(getApplication(), "{My App Secret}",
Analytics.class, Crashes.class);
If you want to catch the crashes from Flutter there might be bit tricky for you because technically you'll have 4 types of crashes.
Android native crash.
iOS Native crash.
Dart crash
Flutter/Dart errors (i know they are not actual crashes but might mess up the app).
Now for point 1, if you want to add AppCenter you can add the code in the onCreate method of the FlutterApplication. You can find it your project -> android -> app -> src -> main -> expand packages
For iOS Native crash you will have to do it in AppDelegate, which is in your project -> iOS -> Runner -> AppDelegate.
For Dart/Flutter crash errors, I don't think there is support from AppCenter. I personally use Sentry and it's doing a pretty good job.
Related
When running Android Studio/ Analyze/ Inspect Code...
I get 50+ identical error messages "Android/Lint/Correctness Error
Upgrade Fragment version to at least 1.3.0" on this line of code:
class MainActivity : AppCompatActivity() {...
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->...
The code breaks on breakpoints, so code is called and the application works correctly.
Build gradle includes (Kotlin language)
implementation "androidx.activity:activity-ktx:1.2.1"
implementation "androidx.fragment:fragment-ktx:1.3.1"
debugImplementation "androidx.fragment:fragment-testing:1.3.1"
I want to FIX the error message (not hide it). Since the code works correctly, and I use fragment-ktx:1.3.1 what should I be looking for?
This has been reported on Google's Issue Tracker and, I quote:
This has been fixed internally and will be available in the next Activity release.
Source: https://issuetracker.google.com/issues/182388985#comment9
TL;DR;
How to add two or more kotlin native modules on an iOS project without getting duplicate symbols error?
The detailed question
Let's assume a multi-module KMP project as a follow where there exists a native app for Android and a native app for iOS and two common modules to hold shared kotlin code.
.
├── android
│ └── app
├── common
│ ├── moduleA
│ └── moduleB
├── ios
│ └── app
The module A contains a data class HelloWorld and has no module dependencies:
package hello.world.modulea
data class HelloWorld(
val message: String
)
Module B contains an extension function for HelloWorld class so it depends on module A:
package hello.world.moduleb
import hello.world.modulea.HelloWorld
fun HelloWorld.egassem() = message.reversed()
The build.gradle configuration of the modules are:
Module A
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"
…
kotlin {
targets {
jvm("android")
def iosClosure = {
binaries {
framework("moduleA")
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
}
cocoapods {…}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
}
iosMain.dependencies {
}
}
}
Module B
apply plugin: "org.jetbrains.kotlin.multiplatform"
apply plugin: "org.jetbrains.kotlin.native.cocoapods"
…
kotlin {
targets {
jvm("android")
def iosClosure = {
binaries {
framework("moduleB")
}
}
if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) {…}
}
cocoapods {…}
sourceSets {
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72"
implementation project(":common:moduleA")
}
androidMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72"
}
iosMain.dependencies {
}
}
}
It looks pretty straightforward and it even works on android if I configure the android build gradle dependencies as a following:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
implementation project(":common:moduleA")
implementation project(":common:moduleB")
}
However, this does not seem to be the correct way to organize multi modules on iOS, because running the ./gradlew podspec I get a BUILD SUCCESSFUL as expected with the following pods:
pod 'moduleA', :path => '…/HelloWorld/common/moduleA'
pod 'moduleB', :path => '…/HelloWorld/common/moduleB'
Even running a pod install I get a success output Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed. whats looks correctly once the Xcode shows the module A and module B on the Pods section.
However, if I try to build the iOS project I get the following error:
Ld …/Hello_World-…/Build/Products/Debug-iphonesimulator/Hello\ World.app/Hello\ World normal x86_64 (in target 'Hello World' from project 'Hello World')
cd …/HelloWorld/ios/app
…
duplicate symbol '_ktypew:kotlin.Any' in:
…/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
…/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
… a lot of duplicate symbol more …
duplicate symbol '_kfun:kotlin.throwOnFailure$stdlib#kotlin.Result<#STAR>.()' in:
…/HelloWorld/common/moduleA/build/cocoapods/framework/moduleA.framework/moduleA(result.o)
…/HelloWorld/common/moduleB/build/cocoapods/framework/moduleB.framework/moduleB(result.o)
ld: 9928 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
My knowledge in iOS is not that much, so to my untrained eyes, it looks like each module is adding its own version of the things instead of using some resolutions strategy to share it.
If I use only the module A the code works and run as expected, so I know the code itself is correct, the problem is how to manage more than 1 module, so that the question, how to add both (module A and module B) on iOS and make things works?
P.S
I did reduce the code as much as I could, trying to keep only the parts that I guess is the source of the problem, however, the complete code is available here if you want to check anything missing in the snippets, or if you want to run and try to solve the problem…
Multiple Kotlin frameworks can be tricky, but should be working as of 1.3.70 which I see you have.
The issue seems to be that both frameworks are static, which is currently an issue in 1.3.70 so it isn't working. (This should be updated by 1.40). It looks like by default the cocoapods plugin sets the frameworks to be static which won't work. I'm unaware of a way to change cocoapods to set it as dynamic but I've tested building without cocoapods and using the isStatic variable in a gradle task, and have gotten an iOS project to compile. Something like:
binaries {
framework("moduleA"){
isStatic = false
}
}
For now you can work around the issue using this method by using the code above and creating a task to build the frameworks(here's an example)
Another thing worth noting is that on the iOS side, the HelloWorld classes will appear as two separate classes despite both coming from moduleA. It's another strange situation with multiple Kotlin frameworks, but I think the extension will still work in this case since you're returning a string.
I actually just wrote up a blog post about multiple Kotlin frameworks that may help with some other questions if you'd like to take a look. https://touchlab.co/multiple-kotlin-frameworks-in-application/
EDIT: Looks like cocoapodsext also has an isStatic variable, so set it to isStatic = false
tl:dr You currently can't have more than one static Kotlin frameworks in the same iOS project. Set them to not be static using isStatic = false.
However, if I try to build the iOS project I get the following error:
This particular error is a known issue. Multiple debug static frameworks are incompatible with compiler caches.
So to workaround the issue you can either disable compiler caches by putting the following line into your gradle.properties:
kotlin.native.cacheKind=none
or make the frameworks dynamic by adding the following snippet to your Gradle build script:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework> {
isStatic = false
}
}
}
See https://youtrack.jetbrains.com/issue/KT-42254 for more details.
I guess current behaviour for multiple frameworks doesn't make much sense for the original topic starter, I'm just putting my answer here for anyone who might encounter the same issue.
My knowledge in iOS is not that much, so to my untrained eyes, it looks like each module is adding its own version of the things instead of using some resolutions strategy to share it.
This is exactly how it is supposed to work at this moment. But "versions of the things" in each of the frameworks are put into the separate independent namespaces, so there should be no linkage errors, and the one you've encountered is a bug.
I am trying to customize the firebase in-app-messaging-display's UI of "Image Only" and "Modal" mode. So I turned to the official documentation, but it is quite simple, by saying:
Creating your own display is a two step process:
1.Write your own implementation of the FirebaseInAppMessagingDisplay class.
2.Register that implemenation with the headless Firebase In-App Messaging SDK.
I wonder how can I import in-app-messaging-display's source code into my project and make it work as a library.
I have downloaded its source code from github:https://github.com/firebase/firebase-android-sdk/tree/master/firebase-inappmessaging-display, tried to import it as a module, but after I selected the Source directory, Android Studio hints that: Specify location of the Gradle
or Android Eclipse project. I also have tried to copy the source code into my project's libs directory and added this: include ':libs:firebase-inappmessaging-display' into my settings.gradle file and this: implementation project(':libs:firebase-inappmessaging-display') into my app's gradle dependency. When sync building Android Studio reports errors like this:
ERROR: Unable to resolve dependency for ':XXXXXXXX': Could not resolve project :libs:firebase-inappmessaging-display.
Any suggestion will be highly appreciated.
The information on the doc is little bit confusing. I am also stuck with the same problem for long time. Actually its very simple.
Add these dependencies in your app level gradle file.
implementation 'com.google.firebase:firebase-core:16.0.8'
implementation ("com.google.firebase:firebase-inappmessaging:17.0.3")
Register Your DisplayMessage component on starting activity.
import com.google.firebase.inappmessaging.FirebaseInAppMessaging
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDisplay
///////
override fun onStart() {
super.onStart()
Log.e("MESSAGE", "activity started")
var firebaseInAppMessagingDisplay = FirebaseInAppMessagingDisplay { inAppMessage, cb ->
// You can show the message here.
// The variable inAppMessage has all information about the campaign that we putting in console (title, content, image url.. etc)
Log.e("MESSAGE", "Display Message callback invoked")
}
FirebaseInAppMessaging.getInstance().setMessageDisplayComponent(firebaseInAppMessagingDisplay)
}
Timber is a great library for logging in Android. In Kotlin classes though, doesn't output anything. How can I fix this?
MainActivity.kt code:
Timber.e("Timber Log 1")
Log.e("MainActivity", "Log 1")
Gradle:
I've tried the regular Java Timber:
implementation 'com.jakewharton.timber:timber:4.7.1'
And this Kotlin specific wrapper:
implementation 'com.github.ajalt:timberkt:1.5.1'
Same result. No output with either. Only from the Log.e()
The first step of Timber is to plant the tree as mentioned in docs
Behavior is added through Tree instances. You can install an instance
by calling Timber.plant. Installation of Trees should be done as early
as possible. The onCreate of your application is the most logical
choice.
And use the debugTree
The DebugTree implementation will automatically figure out from which
class it's being called and use that class name as its tag. Since the
tags vary
If you don't do this then you will have no logs entry and do this as soon as possible, like in oncreate or better inside application class so do this
Timber.plant(Timber.DebugTree());
I have faced same problem, using Kotlin and Android studio 3.6
Follow these steps:
Add the following in build.gradle(Module: App)
implementation 'com.jakewharton.timber:timber:4.7.1'
Initialize Timber in Application Class:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
if(BuildConfig.DEBUG){
Timber.plant(Timber.DebugTree())
}
}
}
Add the Application class(MyApp) to the Manifest (AndroidManifest.xml)
<application
android:name=".MyApp"
Now you can use Timber: Timber.i("Timber logs")
Also can use custom tags if you wish: Timber.tag("Yo").I("used custom tag for logs")
In my case it was wrong BuildConfig import
import org.koin.android.BuildConfig
but my app has
import com.company.example.BuildConfig
Probably late to the party but my problem was the my phone was set to "Charge only" and not "file transfer". Apparently I was allowed to build and run, but logs were blocked
EDIT Another solution:
Check your RUN tab in the bottom of Android Studio. Sometimes the logs get output to there instead
For me it started showing up when I commented the Debug check
// if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
// }
I don't know why this is working because the build varient is selected to debug only.
First of all, thank you for your help.
Here is my problem.
I have made a custom system service (it does connect through the binder, and hooks into the system server, then into the HAL, etc). One of the main commands is isBacklightOn(). I also ran the make update-api to include it in my ROM build.
I am now ready to test the functionality using an app. I am using the following code:
import android.os.BacklightManager; //My service manager name
...
BacklightManager bm = (BacklightManager) getSystemService(BACKLIGHT_SERVICE);
...
if(!bm.isBacklightOn()) {
//Turn it on.
} else {
//Other Things...
}
My problem occurs because Android Studio will not build the application due to not knowing what the BacklightManager is. How can I build this application to test my code? (import something into Android Manager, force a build ignoring errors, etc.)
It looks like the solution is to build a custom SDK within the AOSP build environment. And then import that into Android Studio