e-mission / e-mission-docs

Repository for docs and issues. If you need help, please file an issue here. Public conversations are better for open source projects than private email.
https://e-mission.readthedocs.io/en/latest
BSD 3-Clause "New" or "Revised" License
15 stars 32 forks source link

Repackaging AWARE features as cordova plugins #501

Open xubowenhaoren opened 4 years ago

xubowenhaoren commented 4 years ago

Hello,

After a lengthy trial-and-error of integrating emission into AWARE, we find out that it would be more flexible to merge the AWARE sync adapters and sensors to emission directly. What suggestions do you have for us to get started on the migration?

shankari commented 4 years ago

The high level goal is that we want to modify the AWARE code as little as possible so that we can pull updates from them as they occur. This implies that the ideal structure will involve a wrapper that represents the cordova bits.

First, let us consider the anatomy of an AWARE plugin. @xubowenhaoren pointed to the battery plugin as the easiest to understand.

https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/Battery.java

It looks like:

  1. AWARE does not use SQLite directly but uses the ContentProvider interface instead. The rows are constructed as ContentValues objects and then put into the resolver

    context.getContentResolver().insert(Battery_Data.CONTENT_URI, rowData);
  2. Each ContentProvider (and by extension, each sensor), has its own SyncAdapter associated with it. The sync request is set here ContentResolver.requestSync(request);

So probably the easiest approach is to continue using the ContentProvider architecture for AWARE. In terms of syncing, you can choose to sync to an AWARE server and then merge the data on the server side, or you can write a new SyncAdapter that syncs the data to an e-mission server.

Note that every sensor in AWARE has its own sync adapter. But the base class appears to be https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/syncadapters/AwareSyncAdapter.java Modifying the onPerformSync method of that adapter (or any of the methods it calls, such as offloadData) should make it possible to choose where the data goes to the server.

But that decision is independent of pluginizing the sensor. IMHO, the easiest way to pluginize this code is to leave it untouched and to write a wrapper that inherits from CordovaPlugin. You could either define an external method to start the data collection, which would be consistent with accepting consent and then turning on tracking, or you could just implement the init method of the plugin and have it create the BatteryService and start it.

shankari commented 4 years ago

You could also choose to do this in two steps:

  1. Only pluginize the sensor, and continue to send the data to an AWARE server
  2. After getting that to work, implement a new sync adapter to e-mission if necessary

That will also get you familiar with cordova and reduce the unknown factor.

shankari commented 4 years ago

There are many examples of cordova plugins on the internet. Including the Cordova battery plugin https://github.com/apache/cordova-plugin-battery-status that you can look at for an example of the the cordova plugin interface.

This defines an externally callable method called "start" which sets up the listeners. In your case, that would call onCreate on the AWARE battery service. You would then call start from the javascript during onboarding and everything will Just Work. https://github.com/apache/cordova-plugin-battery-status/blob/master/src/android/BatteryListener.java#L57

I think cordova also has a user guide or some kind of similar documentation.

xubowenhaoren commented 4 years ago

You mentioned that each e-mission plugin only runs after the user consents. So in the repackaging, do I need to check for user contents as well?

shankari commented 4 years ago

It is up to you. You are building your own version of the app and can do whatever you want. Theoretically, you can call markConsented on the e-mission plugin without showing anything to the user since it is just a javascript call.

So it is possible using the e-mission plugin to skip the consent. I just chose to believe that most people will be careful of consent and gave them the tools to handle it properly. e.g. the markConsented method.

So basically if I didn't have the method, then people would have to build it in and everybody is lazy. I made it easier (the default) to start tracking only after consent, and harder to remove it (have to edit onboarding).

shankari commented 4 years ago

@ariasfelipe also wrote a shake detection plugin that used local processing on the phone to detect shakes. This is a much more scalable solution because otherwise the volume of accelerometer data is too high. He demoed it, but I am not sure how well it works in the real world. https://github.com/e-mission/e-mission-shake-detect/

shankari commented 4 years ago

@xubowenhaoren if you just want an example of wrapping native code in a plugin, I would not recommend the shake detect code, since it is much more complex than you need. You should use the step-by-step cordova guide https://cordova.apache.org/docs/en/latest/guide/hybrid/plugins/ combined with the simplistic example https://github.com/ModusCreateOrg/cordova-plugin-example or other tutorials on the internet https://blogs.sap.com/2017/08/18/step-by-step-to-create-a-custom-cordova-plugin-for-android-and-consume-it-in-your-ui5-application/

The shake detect code does allow the user to configure the prompt that is displayed when the shake is detected. What kind of local processing are you planning to do, and what do you want as the resulting output?

xubowenhaoren commented 4 years ago

I want the output to match current AWARE standards, making a functionally equivalent migration. Therefore, I would likely need to follow AWARE's approach of wrapping data. Here is one of their data providers.

shankari commented 4 years ago

with the approach outlined in https://github.com/e-mission/e-mission-docs/issues/501#issuecomment-585910056 you will continue to use the AWARE data format. In fact, you will continue to use their local storage and their server (at least in step 1). So I am not sure what this comment means.

Note that in https://github.com/e-mission/e-mission-docs/issues/501#issuecomment-586369076, I am talking about wrapping code, not data

Update 1:

You don't care about the AWARE implementation details. Once the AWARE services are launched in the background, they will continue to do their stuff in their way. If that changes in the new rewrite, you are not affected.

All you need to do is to wrap the code and launch the aware services properly from the init or exec function of the plugin. Nothing else.

Update 2:

In other words, remember all the lectures on software modularity :) focus on the interface and not the implementation.

xubowenhaoren commented 4 years ago

Thanks for the clarification. It seems like you've made structural changes to the e-mission-docs and I cannot find your notes on how to add a new plugin to a client. The closest I could find is this deployment guide.

shankari commented 4 years ago

@xubowenhaoren I don't think I ever had notes on how to add a new plugin to a client.

Apache Cordova is standard, well-documented technology, and I don't want to duplicate their documentation.

I have added links to the Apache Cordova documentation on creating and managing plugins https://github.com/e-mission/e-mission-docs/issues/501#issuecomment-586369076 along with a link to a third party guide.

xubowenhaoren commented 4 years ago

Now, looking back at what you initially suggested last time and what I've done at this point, I'm afraid that my approach has been somewhat misdirected. What I've done is that I tried to copy over AWARE codes, change them to override e-mission methods and fix any dependency issues along the way. With that said, there are certainly AWARE modules (e.g. SSL certificate helpers) that I don't know how to handle yet.

I think this is NOT what you meant by "creating wrappers" to codes (not data).

xubowenhaoren commented 4 years ago

About the user consent: before we start collectin the user data, we would ask the participant to sign a consent form. This is done before the participants install their apps, and it's very unlikely that the consent criteria need to change during the study. So we don't need to handle consents in this repackaging of the app.

shankari commented 4 years ago

I suggest that you create a cordova plugin with the following interface.

in www/aware.js:

/*global cordova, module*/

var exec = require("cordova/exec")

var Aware = {
}

in src/android/AwarePlugin.java something like this

import aware.....AwareService;

public class AwarePlugin extends CordovaPlugin {
    public static final String TAG = "AwarePlugin";

    @Override
    public void pluginInitialize() {
        AwareService service = new AwareService();
        service.start();
    }
}
xubowenhaoren commented 4 years ago

AWARE clients read aware-core status and modify aware-core settings via a central Aware.settings method. In this repackaging, this would not be directly accessible. Should I then expose Aware.settings in aware.js as JS methods, then use the AwarePlugin.java interface to call the native Aware.settings method?

shankari commented 4 years ago

Yes. In that case, you would want to do something like

in www/aware.js (similar to datacollection.js)

/*global cordova, module*/

var exec = require("cordova/exec")

var Aware = {
    getConfig: function () {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "Aware", "getConfig", []);
        });
    },
    setConfig: function (newConfig) {
        return new Promise(function(resolve, reject) {
            exec(resolve, reject, "Aware", "setConfig", [newConfig]);
        });
    },
}

in src/android/AwarePlugin.java (similar to DataCollectionPlugin.java)

import aware.....AwareService;

public class AwarePlugin extends CordovaPlugin {
    public static final String TAG = "AwarePlugin";

    @Override
    public void pluginInitialize() {
        AwareService service = new AwareService();
        service.start();
    }

    @Override
    public boolean execute(String action, JSONArray data, final CallbackContext callbackContext) throws JSONException {
        if (action.equals("getConfig")) {
            // call AWARE code to get settings
        } else if (action.equals("setConfig")) {
           // call AWARE code to set settings
        }
    }
}
shankari commented 4 years ago

I believe that this is the main AWARE service which launches all the other services depending on the settings. https://github.com/denzilferreira/aware-client/blob/master/aware-core/src/main/java/com/aware/Aware.java

xubowenhaoren commented 4 years ago

Hi,

I realize that the whole aware-core service can be incorporated conveniently via a maven import. Could you point me to an example showing how I can achieve this in a Cordova plugin.xml?

shankari commented 4 years ago

searching for "plugin.xml maven", I find the cordova plugin documentation https://cordova.apache.org/docs/en/dev/plugin_ref/spec.html which includes the example

On Android (as of cordova-android@4.0.0), framework tags are used to include Maven dependencies, or to include bundled library projects.

xubowenhaoren commented 4 years ago

Thanks! But here we have a follow-up problem. Since now we import aware-core services via maven, how should we register the services (e.g. Aware.java) in the Cordova plugin.xml?

shankari commented 4 years ago

With the FQN,

so you would do something like

        <service
            android:name="com.aware....FooService"
            android:enabled="true"
            android:exported="false">
        </service>
xubowenhaoren commented 4 years ago

Another follow-up: in line 119, you are linking the project source codes and res files in the project.xml. Do I need to do this for the aware-core files?

shankari commented 4 years ago

No. Those are only for source code that will be compiled as part of the plugin, not complied libraries.

xubowenhaoren commented 4 years ago

Hello,

I wish to add a (placeholder) UI page that will eventually be replaced by the functional Aware UI. Where can I add this page as a fragment on the current e-mission UI? Is it the main.js?

shankari commented 4 years ago

Probably the easiest is to add a new tab. You can look at examples of current tabs or the angular router documentation.

shankari commented 4 years ago

e.g. https://github.com/e-mission/e-mission-docs/blob/6a0c1ec9ddc31036d64dfc087458bfa46b94cecf/docs/dev/tutorial/small_ui_changes/changes_needed_when_the_set_of_tabs_changes.md

xubowenhaoren commented 4 years ago
  <!-- Diary Tab -->
  <ion-tab title="Diary" icon="ion-map" href="#/root/main/diary">
    <ion-nav-view name="main-diary"></ion-nav-view>
  </ion-tab>

In the Diary Tab above, what does the href attribute refer to? Also, after I put in the web UI files in www/js and www/templates, what do I need to put in main.html to refer to those files?

shankari commented 4 years ago

@xubowenhaoren this is not e-mission specific, this is standard angular-ui-router syntax. There are hundreds of tutorials on angular-ui-router. Please check them out.

xubowenhaoren commented 4 years ago

To check if I get this right:

The main.js has a list of angular.module, each with a $stateProvider$. The main.html defines the actual order of these modules.

Take the root.main.accessmap as an example. In main.js, it is defined as

    .state('root.main.accessmap', {
    url: '/accessmap',
    views: {
      'main-accessmap': {
        templateUrl: 'templates/accessmap/accessmap.html',
        controller: 'AccessMapCtrl'
      }
    },
  })

The templateUrl specifies the HTML location of the page while controller defines the JS elements (e.g. buttons) in js/accessmap/accessmap.js.

As a self-answer to my earlier question , href is for ionic to navigate through pages and not workspace file locations; name in ion-nav-view corresponds to the views in main.js.

Please address any mistakes or missing pieces.

xubowenhaoren commented 4 years ago

I tried to add a placeholder Aware UI page with minimal modification to the AccessMap page. I added aware.js and aware.html. I also added the following config in main.js:

  // aware page
    .state('root.main.aware', {
    url: '/aware',
    views: {
      'main-aware': {
        templateUrl: 'templates/aware/aware.html',
        controller: 'AwareCtrl'
      }
    },
  })

I then followed steps to build a debug Cordova app here. However, when running cordova prepare, I only got the following:

Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/hooks/after_prepare/010_add_platform_class.js /Users/bowenxu2/Documents/GitHub/e-mission-phone
add to body class: platform-android
add to body class: platform-ios
Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/hooks/after_prepare/015_copy_icon_to_drawable.js /Users/bowenxu2/Documents/GitHub/e-mission-phone
About to copy file /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/res/mipmap-hdpi/icon.png -> /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/res/drawable-hdpi/icon.png
... Other res copying logs ... 
Running command: /Users/bowenxu2/Documents/GitHub/e-mission-phone/package-hooks/ios9_allow_http.sh /Users/bowenxu2/Documents/GitHub/e-mission-phone

That was it. No Creating Cordova project for the Android platform: of any kind.

I am running ionic 3.19.1 and Cordova 8.1.2. What am I missing?

shankari commented 4 years ago

You are not reading the e-mission-phone specific instructions which cover using the same codebase for UI development as well as building a native app. https://github.com/e-mission/e-mission-phone#installation in particular the ./bin/configure_xml_and_json.js step

xubowenhaoren commented 4 years ago

After some debugging, I found that the issue is most closely related to the local Aware plugin that I'm working on.

However, I think I messed up my local Cordova project so that even after running cordova plugin rm edu.berkeley.eecs.emission.cordova.aware, the dependency compile "com.github.denzilferreira:aware-client:$aware_libs" still shows up when I try cordova build android. I will clean up and reset the local project and try again..

xubowenhaoren commented 4 years ago

To detect where exactly the error is, I reset my local e-mission workspace to match the latest opentoall branch. I followed the steps in the installation guide.

Then I replaced config.xml and package.json with my own (only changes are the package names/id). I also added the google-services.json and GoogleService-Info.plist that were tested to work in 2019. Note that I did this after running

./bin/configure_xml_and_json.js cordovabuild

As suggested by this guide here.

However, even with no other modification, the build had failed:

:transformClassesWithDexBuilderForDebugcom.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
    at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
    at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:720)
    at com.android.ide.common.internal.WaitableExecutor.waitForTasksWithQuickFail(WaitableExecutor.java:146)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.transform(DexArchiveBuilderTransform.java:235)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:222)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:218)
    at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
    at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:213)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:173)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:121)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:122)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:111)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:63)
    at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:124)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:80)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:105)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:99)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:99)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:550)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.lambda$convertToDexArchive$1(DexArchiveBuilderTransform.java:488)
    at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Error while dexing module-info.class

    at com.android.builder.dexing.DxDexArchiveBuilder.getExceptionToRethrow(DxDexArchiveBuilder.java:143)
    at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:89)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:545)
    ... 6 more
Caused by: com.android.dx.cf.iface.ParseException: unsupported class file version 53.0
    at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:499)
    at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:420)
    at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:402)
    at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:253)
    at com.android.builder.dexing.DxDexArchiveBuilder.dex(DxDexArchiveBuilder.java:99)
    at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:86)
    ... 7 more

 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformClassesWithDexBuilderForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 4s
35 actionable tasks: 2 executed, 33 up-to-date
(node:42514) UnhandledPromiseRejectionWarning: Error: /Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/gradlew: Command failed with exit code 1 Error output:
com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
    at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
    at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:720)
    at com.android.ide.common.internal.WaitableExecutor.waitForTasksWithQuickFail(WaitableExecutor.java:146)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.transform(DexArchiveBuilderTransform.java:235)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:222)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:218)
    at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
    at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:213)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:173)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:121)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:122)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:111)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:63)
    at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:124)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:80)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:105)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:99)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:99)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:550)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.lambda$convertToDexArchive$1(DexArchiveBuilderTransform.java:488)
    at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: com.android.builder.dexing.DexArchiveBuilderException: Error while dexing module-info.class

    at com.android.builder.dexing.DxDexArchiveBuilder.getExceptionToRethrow(DxDexArchiveBuilder.java:143)
    at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:89)
    at com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform.launchProcessing(DexArchiveBuilderTransform.java:545)
    ... 6 more
Caused by: com.android.dx.cf.iface.ParseException: unsupported class file version 53.0
    at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:499)
    at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:420)
    at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:402)
    at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:253)
    at com.android.builder.dexing.DxDexArchiveBuilder.dex(DxDexArchiveBuilder.java:99)
    at com.android.builder.dexing.DxDexArchiveBuilder.convert(DxDexArchiveBuilder.java:86)
    ... 7 more

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':transformClassesWithDexBuilderForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process /Users/bowenxu2/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.6/9180733b7df8542621dc12e21e87557e8c99b8cb/gson-2.8.6.jar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 4s
    at ChildProcess.whenDone (/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/cordova/node_modules/cordova-common/src/superspawn.js:169:23)
    at ChildProcess.emit (events.js:189:13)
    at maybeClose (internal/child_process.js:970:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5)
(node:42514) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
shankari commented 4 years ago

Did you search the previous issues? https://github.com/e-mission/e-mission-docs/search?q=DexArchiveBuilderException&type=Issues This has been reported earlier and there is a workaround listed there. I have also checked in that workaround into the data collection plugin. Check the release notes for details. You can't just use the most recent version.

xubowenhaoren commented 4 years ago

We figured that the aware-client relies on jitpack.io as the maven URL. We have figured a workaround:

In the project build.gradle, add maven { url 'https://jitpack.io' } to all repositories. Here is our example:

buildscript {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
        maven {
          url 'https://jitpack.io'
        }
    }

    // Switch the Android Gradle plugin version requirement depending on the
    // installed version of Gradle. This dependency is documented at
    // http://tools.android.com/tech-docs/new-build-system/version-compatibility
    // and https://issues.apache.org/jira/browse/CB-8143
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
    }
}

// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
    repositories {
        jcenter()
        maven {
            url "https://maven.google.com"
        }
        maven {
          url 'https://jitpack.io'
        }
    }
}

The next step is to automate this step in the plugin so that this can happen without a manual change in the project level.

shankari commented 4 years ago

here's an example of an extra gradle file included in the plugin that can be included in the overall project gradle file

https://github.com/e-mission/cordova-server-communication/tree/master/src/android/httplib.gradle

Now you have to figure out how to make it work for this gradle command.

xubowenhaoren commented 4 years ago

Issue we had today:

error: style attribute 'android:attr/dialogCornerRadius' not found.
error: resource android:attr/fontVariationSettings not found.
error: resource android:attr/ttcIndex not found.
error: resource android:attr/lineHeight not found.
error: resource android:attr/textFontWeight not found.

What we tried: In build.gradle, add compileSdkVersion 28. It didn't work.

What to expect: aware-client switched to use AndroidX last year. The mismatch between AndroidX and legacy android appcompact could be the issue. There is an option in Android Studio to auto-convert all dependencies to AndroidX. I will try this and update it later.

xubowenhaoren commented 4 years ago

Auto-conversion to either AndroidX or appcompact didn't work. I will update more findings later.

shankari commented 4 years ago

It looks like it may be related to the version of the support libraries https://github.com/apache/cordova-android/issues/591

Looking through the places where we include the support libraries, it looks like we do indeed include a bunch of support libraries with various versions of + in there. Including one from my plugin - e.g. edu.berkeley.eecs.emission.cordova.unifiedlogger.

However, without the AWARE plugin, there are no build issues - see working CI in the new repo https://github.com/covid19database/phone-app Does the AWARE plugin introduce a newer version of the support library?

Can you see which versions of the support library are in the gradle file and try to pare it down to one?

plugins//cordova-plugin-local-notification/plugin.xml:        <framework src="com.android.support:support-v4:26.+" />
plugins//cordova-plugin-email-composer/plugin.xml:        <framework src="com.android.support:support-v4:24.1.1+" />
plugins//edu.berkeley.eecs.emission.cordova.unifiedlogger/plugin.xml:    <framework src="com.android.support:support-v4:+" />
plugins//edu.berkeley.eecs.emission.cordova.auth/src/android/openid-config.gradle:    resolutionStrategy.force 'com.android.support:support-v4:26.1.0'
xubowenhaoren commented 4 years ago

Does the AWARE plugin introduce a newer version of the support library?

Yes. Here is a snippet from the aware-core build.gradle:

    implementation "androidx.gridlayout:gridlayout:1.0.0"
    implementation "androidx.cardview:cardview:1.0.0"

    implementation "androidx.annotation:annotation:1.1.0"
    implementation "androidx.core:core:1.2.0-rc01"
    implementation "com.google.android.material:material:1.2.0-alpha02"
    implementation "androidx.appcompat:appcompat:1.1.0"

We can see that the aware-core is indeed using Android. To be specific, in the artifact mapping table, com.android.support:support-compat is indeed androidx.core:core.

I tried to force the dependencies in the Cordova project build.gradle to stick to SDK 28, but Android Studio still gave the following warnings about mixed usage.

    implementation fileTree(dir: 'libs', include: '*.jar')
    // SUB-PROJECT DEPENDENCIES START
    implementation(project(path: "CordovaLib"))
    compile "com.android.support:support-v13:28.0.3"
    compile "me.leolin:ShortcutBadger:1.1.17@aar"
    compile "com.google.firebase:firebase-messaging:17.0.0"
    compile "com.android.support:support-v4:28.1.1+"
    compile "com.android.support:support-v4:+"
    compile "com.google.android.gms:play-services-auth:17.0.0"
    compile "net.openid:appauth:0.7.0"
    compile "com.auth0.android:jwtdecode:1.1.1"
    compile "com.google.code.gson:gson:2.8.5"
    compile "com.google.android.gms:play-services-location:17.0.0"
    compile "com.github.denzilferreira:aware-client:master-SNAPSHOT"

image

It looks like I might need to change the plugin gradle files or find a "global" way to force the usage of either AndroidX or the legacy ones.

shankari commented 4 years ago

aha! did you try this or similar solutions? https://stackoverflow.com/a/55801213/4040267

xubowenhaoren commented 4 years ago

Before I had the opportunity to try that solution, I erroneously used both "Migrate to AndroidX" and "Migrate to appcompact" in Android Studio. To recover from that mistake, I thought it was best to start over from a clean e-mission project.

Thus I cloned a fresh copy and ran the procedures on the guide. I was aware of the gson issue, so in my package.json, I had

  "dependencies": {
    ...
    "e-mission-data-collection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.4",
    ...
  }

However, when I ran cordova prepare, I saw the following error:

Discovered plugin "edu.berkeley.eecs.emission.cordova.datacollection" in config.xml. Adding it to the project
Failed to restore plugin "edu.berkeley.eecs.emission.cordova.datacollection" from config.xml. You might need to try adding it again. Error: Error: npm: Command failed with exit code 1 Error output:
npm ERR! code E404
npm ERR! 404 'edu.berkeley.eecs.emission.cordova.datacollection' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it
npm ERR! 404 (or use the name yourself!)
npm ERR! 404 
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
npm ERR! 404 
npm ERR! 404  'edu.berkeley.eecs.emission.cordova.datacollection@latest' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)
npm ERR! 404 
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/bowenxu2/.npm/_logs/2020-04-16T05_06_44_530Z-debug.log

Do I need to also change some option in config.xml?

shankari commented 4 years ago

plugins are specified in both config.xml and package.json. which is why configure_xml_and_json copies them both over. Did you follow the instructions exactly?

xubowenhaoren commented 4 years ago

plugins are specified in both config.xml and package.json. which is why configure_xml_and_json copies them both over. Did you follow the instructions exactly?

I previously had custom config.xml and package.json. I have updated them correctly with the new data collection plugin. Thanks for the tip!

xubowenhaoren commented 4 years ago

aha! did you try this or similar solutions? https://stackoverflow.com/a/55801213/4040267

I tried. Logs below.

$ cordova plugin add cordova-plugin-androidx
Plugin doesn't support this project's cordova-android version. cordova-android: 6.4.0, failed version requirement: >=8.0.0
Skipping 'cordova-plugin-androidx' for android

$ cordova platform update android@8.0.0
Using cordova-fetch for cordova-android@8.0.0
Updating android project...
(node:72372) UnhandledPromiseRejectionWarning: An in-place platform update is not supported. 
The `platforms` folder is always treated as a build artifact in the CLI workflow.
To update your platform, you have to remove, then add your android platform again.
Make sure you save your plugins beforehand using `cordova plugin save`, and save 
a copy of the platform first if you had manual changes in it.
    cordova plugin save
    cordova platform rm android
    cordova platform add android

$ cordova plugin save
$ cordova platform rm android
$ cordova platform add android@8.0.0
... (no errors)

$ cordova plugin add cordova-plugin-androidx
Plugin "cordova-plugin-androidx" already installed on android.
Plugin "cordova-plugin-androidx" already installed on ios.
Adding cordova-plugin-androidx to package.json
Saved plugin info for "cordova-plugin-androidx" to config.xml

$ cordova plugin add cordova-plugin-androidx-adapter
Installing "cordova-plugin-androidx-adapter" for android
Installing "cordova-plugin-androidx-adapter" for ios
Adding cordova-plugin-androidx-adapter to package.json
Saved plugin info for "cordova-plugin-androidx-adapter" to config.xml

Then I opened up CordovaLib in Android Studio and tried to build.

FAILURE: Build completed with 2 failures.

1: Task failed with an exception.
-----------
* Where:
Script '/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle' line: 121

* What went wrong:
A problem occurred evaluating project ':app'.
> Unable to determine Android SDK directory.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating project ':app'.
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:92)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl$2.run(DefaultScriptPluginFactory.java:206)
    at org.gradle.configuration.ProjectScriptTarget.addConfiguration(ProjectScriptTarget.java:77)
    at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:211)
    at org.gradle.configuration.BuildOperationScriptPlugin$1$1.run(BuildOperationScriptPlugin.java:69)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:66)
    at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:63)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:48)
    at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:63)
    at org.gradle.configuration.project.BuildScriptProcessor$1.run(BuildScriptProcessor.java:44)
    at org.gradle.internal.Factories$1.create(Factories.java:25)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:200)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
    at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:41)
    at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
    at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:34)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:106)
    at org.gradle.internal.Factories$1.create(Factories.java:25)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
    at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:226)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:220)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:95)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:67)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:695)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:143)
    at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:35)
    at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:62)
    at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:41)
    at org.gradle.initialization.DefaultGradleLauncher$ConfigureBuild.run(DefaultGradleLauncher.java:302)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.initialization.DefaultGradleLauncher.configureBuild(DefaultGradleLauncher.java:210)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
    at org.gradle.initialization.DefaultGradleLauncher.getConfiguredBuild(DefaultGradleLauncher.java:129)
    at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:67)
    at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:64)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:82)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:75)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
    at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
    at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:75)
    at org.gradle.internal.invocation.GradleBuildController.configure(GradleBuildController.java:64)
    at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:62)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:58)
    at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
    at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:49)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:46)
    at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:78)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:46)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:59)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:36)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:81)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: java.lang.RuntimeException: Unable to determine Android SDK directory.
    at cordova_b854vvclv68kswzvar16ge5n9.getAndroidSdkDir(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:121)
    at cordova_b854vvclv68kswzvar16ge5n9.getAvailableBuildTools(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:43)
    at cordova_b854vvclv68kswzvar16ge5n9.doFindLatestInstalledBuildTools(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:52)
    at cordova_b854vvclv68kswzvar16ge5n9$_run_closure1$_closure13.doCall(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle:192)
    at cordova_b854vvclv68kswzvar16ge5n9$_run_closure1$_closure13.doCall(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/CordovaLib/cordova.gradle)
    at org.gradle.internal.extensibility.DefaultExtraPropertiesExtension.methodMissing(DefaultExtraPropertiesExtension.java:83)
    at org.gradle.internal.metaobject.BeanDynamicObject$GroovyObjectAdapter.invokeOpaqueMethod(BeanDynamicObject.java:579)
    at org.gradle.internal.metaobject.BeanDynamicObject$MetaClassAdapter.invokeMethod(BeanDynamicObject.java:506)
    at org.gradle.internal.metaobject.BeanDynamicObject.tryInvokeMethod(BeanDynamicObject.java:191)
    at org.gradle.internal.metaobject.ConfigureDelegate.invokeMethod(ConfigureDelegate.java:56)
    at build_276wgwbbmkl5cfo7tgv5i3r8m.run(/Users/bowenxu2/Documents/GitHub/e-mission-phone/platforms/android/app/build.gradle:114)
    at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:90)
    ... 128 more

==============================================================================

2: Task failed with an exception.
-----------
* What went wrong:
A problem occurred configuring project ':app'.
> compileSdkVersion is not specified.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':app'.
    at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:79)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.addConfigurationFailure(LifecycleProjectEvaluator.java:72)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.access$600(LifecycleProjectEvaluator.java:53)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:108)
    at org.gradle.internal.Factories$1.create(Factories.java:25)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
    at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:226)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:220)
    at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:186)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:95)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:67)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:695)
    at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:143)
    at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:35)
    at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:62)
    at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:41)
    at org.gradle.initialization.DefaultGradleLauncher$ConfigureBuild.run(DefaultGradleLauncher.java:302)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.initialization.DefaultGradleLauncher.configureBuild(DefaultGradleLauncher.java:210)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
    at org.gradle.initialization.DefaultGradleLauncher.getConfiguredBuild(DefaultGradleLauncher.java:129)
    at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:67)
    at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:64)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:82)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:75)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
    at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
    at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:75)
    at org.gradle.internal.invocation.GradleBuildController.configure(GradleBuildController.java:64)
    at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:62)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:58)
    at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
    at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:49)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:46)
    at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:78)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:46)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:59)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:36)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:81)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: compileSdkVersion is not specified.
    at com.google.common.base.Preconditions.checkState(Preconditions.java:507)
    at com.android.build.gradle.BasePlugin.createAndroidTasks(BasePlugin.java:693)
    at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:81)
    at com.android.build.gradle.BasePlugin.lambda$createTasks$4(BasePlugin.java:651)
    at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt:37)
    at com.android.build.gradle.internal.crash.CrashReporting$afterEvaluate$1.execute(crash_reporting.kt)
    at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1$1.run(DefaultListenerBuildOperationDecorator.java:150)
    at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:58)
    at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction$1.run(DefaultListenerBuildOperationDecorator.java:147)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.configuration.internal.DefaultListenerBuildOperationDecorator$BuildOperationEmittingAction.execute(DefaultListenerBuildOperationDecorator.java:144)
    at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:91)
    at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:80)
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:42)
    at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:230)
    at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:149)
    at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:58)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:324)
    at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:234)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:140)
    at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy36.afterEvaluate(Unknown Source)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:190)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate$1.execute(LifecycleProjectEvaluator.java:187)
    at org.gradle.api.internal.project.DefaultProject.stepEvaluationListener(DefaultProject.java:1424)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$NotifyAfterEvaluate.run(LifecycleProjectEvaluator.java:196)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:111)
    ... 108 more

==============================================================================

* Get more help at https://help.gradle.org

I also noticed that the gradle files are much different. They are too long so I attached them. Archive.zip

xubowenhaoren commented 4 years ago

Note that the issue java.lang.RuntimeException: Unable to determine Android SDK directory. shouldn't happen; I checked my SDK directory and it was correctly configured.

shankari commented 4 years ago

@xubowenhaoren, upgrading to cordova-android@8.0.0 is not that easy because it introduces incompatibilities in the build process.

$ cordova platform update android@8.0.0

As I said during our conversation, I have upgraded the COVID-19 version (https://github.com/covid19database/phone-app) to the latest version, but not had the time to port the changes over to e-mission phone yet. I started porting yesterday, but just got a bunch of higher priority work on my plate today, so it probably won't get done until tonight/tomorrow morning.

If you want to manually make the changes there yourself, then feel free to look at the changes in that repo and copy them over!

xubowenhaoren commented 4 years ago

Besides upgraded plugin versions in config.xml and package.json, what are the major changes that I need to make? It's pretty hard to track 1000+ commits.

shankari commented 4 years ago

most of the 1000+ commits are already from e-mission-phone since https://github.com/covid19database/phone-app started with e-mission as the base. You just need to find the new commits.

Last night, I created a fork of e-mission-phone and pulled all the changes from the phone-app https://github.com/covid19database/e-mission-phone That should make it much easier for you to see the differences.

xubowenhaoren commented 4 years ago

I just observed an unexpected behavior for using source setup/setup_android_native.sh in the COVID-19 version. If the user already has Gradle 4.1, then

Setting up gradle using SDKMan

Stop! gradle 4.1 is already installed.
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

The shell script does not progress forward and run source setup/setup_shared_native.sh. I am using macOS 10.15.3.

P.S. I know that this is not the right place to submit this "issue". However, I didn't find the issue tab in the COVID-19 version.