I have a project which supports API 19 and higher. I want to implement jetpack compose to the project but i want to keep supporting below API 21. For this purpose, I created two flavors: "minApi21", "minApi19". I want minApi21 to support compose while minApi19 works with the old code.
Here is the build.gradle file:
android {
.
.
.
buildTypes {
debug {
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
.
.
.
flavorDimensions "api"
productFlavors{
minApi21 {
dimension "api"
minSdkVersion 21
versionCode 2000 + android.defaultConfig.versionCode
}
minApi19 {
dimension "api"
minSdkVersion 19
versionCode 1000 + android.defaultConfig.versionCode
versionNameSuffix ".19"
}
}
.
.
.
if (getGradle().getStartParameter().getTaskRequests()
.toString().contains("MinApi21")){
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerVersion = "1.5.31"
kotlinCompilerExtensionVersion = "1.1.0-alpha05"
}
}
}
Here is the dependencies:
dependencies{
.
.
.
minApi21Implementation 'androidx.activity:activity-compose:1.3.1'
// Integration with activities
minApi21Implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"
minApi21Implementation "androidx.compose.runtime:runtime:1.1.0-alpha05"
// Compose Material Design
minApi21Implementation 'androidx.compose.material:material:1.0.2'
// Animations
minApi21Implementation "androidx.navigation:navigation-compose:2.4.0-alpha09"
minApi21Implementation 'androidx.compose.animation:animation:1.0.2'
// Tooling support (Previews, etc.)
minApi21Implementation "com.github.skydoves:landscapist-fresco:1.3.6"
minApi21Implementation "androidx.compose.ui:ui-tooling:1.0.2"
// Integration with ViewModels
minApi21Implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-beta01'
minApi21Implementation "com.google.accompanist:accompanist-permissions:0.18.0"
minApi21Implementation "androidx.compose.ui:ui:1.0.2"
minApi21Implementation "androidx.compose.ui:ui-tooling-preview:1.0.2"
minApi21Implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
androidTestMinApi21Implementation 'androidx.compose.ui:ui-test-junit4:1.0.2'
androidTestMinApi21Implementation "androidx.test.espresso:espresso-core:$espressoVersion"
androidTestMinApi21Implementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
androidTestMinApi21Implementation "androidx.compose.ui:ui-test-junit4:1.0.2"
}
With this build.gradle file, both minApi19 and minApi21 debug versions build perfectly fine, but when i create signed bundles i got the error message:
Task :app:compileMinApi19ReleaseKotlin FAILED
androidx.compose.compiler.plugins.kotlin.IncompatibleComposeRuntimeVersionException: The Compose Compiler requires the Compose Runtime to be on the class path, but none could be found. The compose compiler plugin you are using (version 1.1.0-alpha05) expects a minimum runtime version of 1.0.0.
at androidx.compose.compiler.plugins.kotlin.VersionChecker.noRuntimeOnClasspathError(VersionChecker.kt:112)
at androidx.compose.compiler.plugins.kotlin.VersionChecker.check(VersionChecker.kt:89)
at androidx.compose.compiler.plugins.kotlin.ComposeIrGenerationExtension.generate(ComposeIrGenerationExtension.kt:61)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr$lambda-1(JvmIrCodegenFactory.kt:126)
at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:89)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr(JvmIrCodegenFactory.kt:146)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.convertToIr$default(JvmIrCodegenFactory.kt:64)
at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.generateModule(JvmIrCodegenFactory.kt:59)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:35)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:321)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:113)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:56)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:169)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:92)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:412)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:112)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:358)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally$default(IncrementalCompilerRunner.kt:300)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl$rebuild(IncrementalCompilerRunner.kt:119)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:170)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:81)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:607)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:96)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1658)
I tried to declare compileOptions and buildFeatures in flavor, got the same error.
How can i solve this issue and why it fails only in release version?
We're also playing around Jetpack Compose on an opensource project and had the same issue which solved by actually doing something like this only for the runtime part,
if (gradle.startParameter.taskNames.any { "minApi21" in it || "MinApi21" in it }) {
implementation("androidx.compose.runtime:runtime:$composeVersion")
}
(it is in Kotlin DSL not Groovy so you have to do the conversion yourself)
And here is the part I've done it, https://github.com/persian-calendar/DroidPersianCalendar/blob/2af5eb3/PersianCalendar/build.gradle.kts#L200
I know it is not ideal but was is good enough for us until compose itself gets useful enough so abandoning Android 4 worth the benefits of compose brings on the table and this makes it the switch actually worthy but this hacky solution makes the signed builds of separated flavor for compose actually work.
Related
I have an Android game developed with LibGdx version 1.9.9, which I am trying to export in HTML. I am using GWT (V-2.8.2). The game is running well in Android and doesn't have any issues. While exporting the game by running this command ./gradlew html:dist I am not getting any errors.
But when I am placing the exported library into the localhost and trying to access the game, first the default loader is appearing and then there is a blank screen with this error message:
GwtApplication: exception: (TypeError) : null is not an object (evaluating 'null.zY')
(TypeError) : null is not an object (evaluating 'null.zY')
This is happening in every browser - Safari, Chrome, Firefox.
The stack trace doesn't show any significant place of debug.
Any idea of what is the problem? Thanks.
HTML Gradle:
gwt {
gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend
maxHeapSize="2G" // Default 256m is not enough for gwt compiler. GWT is HUNGRY
minHeapSize="1G"
src = files(file("src/")) // Needs to be in front of "modules" below.
modules 'com.package.gamename.GdxDefinition'
devModules 'com.package.gamename.GdxDefinitionSuperdev'
project.webAppDirName = 'webapp'
compiler {
strict = true;
disableCastChecking = true;
}
}
import org.wisepersist.gradle.plugins.gwt.GwtSuperDev
def HttpFileServer server = null
def httpFilePort = 8080
task startHttpServer () {
dependsOn draftCompileGwt
String output = project.buildDir.path + "/gwt/draftOut"
doLast {
copy {
from "webapp"
into output
}
copy {
from "war"
into output
}
server = new SimpleHttpFileServerFactory().start(new File(output), httpFilePort)
println "Server started in directory " + server.getContentRoot() + ", http://localhost:" + server.getPort()
}
}
task superDev (type: GwtSuperDev) {
dependsOn startHttpServer
doFirst {
gwt.modules = gwt.devModules
}
}
task dist(dependsOn: [clean, compileGwt]) {
doLast {
file("build/dist").mkdirs()
copy {
from "build/gwt/out"
into "build/dist"
}
copy {
from "webapp"
into "build/dist"
}
copy {
from "war"
into "build/dist"
}
}
}
task addSource {
doLast {
sourceSets.main.compileClasspath += files(project(':core').sourceSets.main.allJava.srcDirs)
}
}
tasks.compileGwt.dependsOn(addSource)
tasks.draftCompileGwt.dependsOn(addSource)
sourceCompatibility = 1.6
sourceSets.main.java.srcDirs = [ "src/" ]
eclipse.project {
name = appName + "-html"
}
Enter the superdev mode and activate source mapping and debugging and step through the source in Chrome, that's the way to find these problems.
Start with superdev parameter
Open the game's web page
Hit the arrow button at the top left corner
Hit the "compile" button
Source Maps are available in Chrome now, you get a "real" stack trace.
Good afternoon.
At the moment I am trying to write the code in the "Main Activity" to send some waypoints to my IRIS drone but it is only working when the points are five. Could you check my code and give me suggestions about what is happening and how can I send more waypoints to my drone? I really appreciate your help because I am new developing in Android:
Code:
public void onBtnConnectTap3(View view) {
if (this.drone.isConnected()) {
this.drone.disconnect();
} else {
Spinner connectionSelector = (Spinner) findViewById(R.id.selectConnectionType);
int selectedConnectionType = connectionSelector.getSelectedItemPosition();
Bundle extraParams = new Bundle();
if (selectedConnectionType == ConnectionType.TYPE_USB) {
extraParams.putInt(ConnectionType.EXTRA_USB_BAUD_RATE, DEFAULT_USB_BAUD_RATE); // Set default baud rate to 57600
} else {
extraParams.putInt(ConnectionType.EXTRA_UDP_SERVER_PORT, DEFAULT_UDP_PORT); // Set default baud rate to 14550
}
ConnectionParameter connectionParams = new ConnectionParameter(selectedConnectionType, extraParams, null);
this.drone.connect(connectionParams);
}
currentMission = new Mission();
currentMission.clear();
for (int i = 1; i < 20; i++) {
waypoint2=new Waypoint();
yaw=new YawCondition();
waypoint2.setCoordinate(new LatLongAlt( i, i, i));
yaw.setAngle(i);
missionI3 = waypoint2;
currentMission.addMissionItem(missionI3);
missionI2=yaw;
currentMission.addMissionItem(missionI2);
}
this.drone.generateDronie();
this.drone.setMission(currentMission, true);
this.drone.arm(true);
}
Dependencies in Build.gradle:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.o3dr.android:dronekit-android:2.3.11'
}
I would like to know if you also know where I can keep learning about how to develop apps in Android for 3DRobotics drones taking in consideration that my main sources are: http://android.dronekit.io/first_app.html and http://android.dronekit.io/javadoc/
Thanks in advance for your answer.
I'm not completely sure what you are trying to accomplish, but I see some possible errors in your code.
Use the latest of dronekit-android. The current version is 2.7.0. You can keep up to date on the versions here https://bintray.com/3drobotics/maven/dronekit-android/view
You are generating a mission with 38 items (19 waypoints, and 19 yaws). You are doing a very unsafe thing by setting waypoint coordinates to 1,1,1 ... 19,19,19. You vehicle will fly somewhere I assume you didn't intend.
I'm unsure why you have generateDronie(). As per the docs
Generate action to create a dronie mission, and upload it to the connected drone.
A dronie is a specific type mission that will fly a selfie path.
setMission() is correct. However, the last step in your code is to arm the vehicle. You will need to tell the drone to actually run the mission. You can do this with the startMission() method in the MissionApi class.
Be careful setting and starting mission with the same user interaction. There is always the chance that setMission() will fail to upload to the vehicle. If this is the case, startMission() will run the last mission that was successfully uploaded to the vehicle.
You can verify the upload succeeded by listening for the broadcast AttributeEvent.MISSION_SENT.
You can always contribute to the documentation by adding javadocs to APIs that you feel are missing or need clarification.
I would like to deploy my web application to several environments. Using Continuous Integration I can run a task to generate a config.json for a particular environment. This file will contain, among others, the particular URLs to use for it.
{
"baseUrl": "http://www.myapp.es/",
"baseApiUrl": "http://api.myapp.es/",
"baseAuthUrl": "http://api.myapp.es/auth/"
}
The issue comes up when I try to set my different services through providers in the config phase. Of course, services are not available yet in the phase so I cannot use $http to load that json file and set my providers correctly.
Basically I would like to do something like:
function config($authProvider) {
$authProvider.baseUrl = config.baseAuthUrl;
}
Is there a way to load those values on runtime from a file? The only thing I can think about is having that mentioned task altering this file straight away. However I have several modules and therefore, that would have to do in all of them which doesn´t seem right.
You can create constants in the config of your main module:
Add $provide as a dependency in your config method
use the provider method to add all constants like this
$provide.provider('BASE_API_URL', {
$get: function () {
return 'https://myexample.net/api/';
}
});
You can use BASE_API_URL as a dependency in your services.
I hope this helps
Optionally you can set the url depending of your environment:
$provide.provider('BASE_API_URL', {
$get: function () {
if(window.location.hostname.toLowerCase() == 'myapp.myexample.net')
{
return 'https://myexample.net/api/' //pre-production
}else
{
return 'http://localhost:61132/'; //local
}
}
});
Regards!
Finally, the solution was generating an angular constants file using templating (gulp-template) through a gulp task. At the end, I am using a yaml file instead a json one (which is the one generated my CI engine with the proper values for the environment I want to deploy to).
Basically:
config.yml
baseUrl: 'http://www.myapp.es/'
baseApiUrl: 'http://api.myapp.es/'
auth:
url: 'auth/'
config.module.constants.template
(function () {
'use strict';
angular
.module('app.config')
.constant('env_variables', {
baseUrl: '<%=baseUrl%>',
baseApiUrl: '<%=baseApiUrl%>',
authUrl: '<%=auth.url%>'
});
}());
gulpfile.js
gulp.task('splicing', function(done) {
var yml = path.join(conf.paths.src, '../config/config.yml');
var json = yaml.safeLoad(fs.readFileSync(yml, 'utf8'));
var template = path.join(conf.paths.src, '../config/config.module.constants.template');
var targetFile = path.join(conf.paths.src, '/app/config');
return gulp.src(template)
.pipe($.template(json))
.pipe($.rename("config.module.constants.js"))
.pipe(gulp.dest(targetFile), done);
});
Then you just inject it in the config phase you need:
function config($authProvider, env_variables) {
$authProvider.baseUrl = env_variables.baseApiUrl + env_variables.authUrl;
}
One more benefit about using gulp for this need is that you can integrate the generation of these constants with your build, serve or watch tasks and literally, forget about doing any change from now on. Hope it helps!
Hello i have a project that uses gulp for the build framework, and used karma with jasmine for the testing.
I am trying to integrate proxyquireify to mock the requires, i just added proxyquireify as browserify plugin in karma config, as i am using karma-browserify.
But this results in an error when running the tests, in the first line, saying 'require is undefined'.
What am i doing wrong?
here is my karma config
// Karma configuration
// Generated on Wed Nov 26 2014 17:57:28 GMT+0530 (IST)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['browserify', 'jasmine'],
// list of files / patterns to load in the browser
files: [
'./components/renderer/**/*.spec.js',
'./components/tracker/**/*.spec.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'./components/**/*.spec.js' : [ 'browserify' ]
},
browserify: {
debug:true,
plugin: ['proxyquireify/plugin']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['spec'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
proxyquireify works internally by substituting the require function provided by browserify.
In this case it seems the new substituted require function was not exposed to global scope.
I went through the code and found out proxyquireify creates the new require function in node_modules/proxyquireify/lib/prelude.js named as newRequire.
the issue i was having was that the newRequire function was not exposed in the global scope as the require function, so i changed node_modules/proxyquireify/lib/prelude.js so that
// Override the current require with this new one
return newRequire;
becomes
// Override the current require with this new one
require = newRequire;
and the newRequire was properly exposed to global scope and everything worked fine. Since this change is reset every time i do a npm install, i created a gulp task in my case which does this change every time before tests are run, i will add the gulp task for reference
// Task to modify proxyquireify so that it works properly, there is a bug in the npm library
gulp.task('_patch:proxyquireify', function() {
gulp.src(['./node_modules/proxyquireify/lib/prelude.js'])
.pipe(replace(/return newRequire;/g, 'require = newRequire;'))
.pipe(gulp.dest('./node_modules/proxyquireify/lib'));
});
I run this task before executing the test tasks, like this
// Task to run tests
gulp.task('run:test', ['_patch:proxyquireify'], function() {
//logic to run tests
};
I hope this helps, thanks
in create_project.py
# Create cross-platform cocos2d-x project
# define global variables
PLATFORMS = {
"cpp" : ["ios", "android", "win32", "mac", "linux"],
}
I can not find out the parameter to create the cocos2dx project for wp8/winrt.
Your comment welcome
My create_project.py file looks exactly like this: http://www.cocos2d-x.org/attachments/2790/create_project.py (I use version 2.2.1). If you specify cpp in the parameters, it creates wind32 and winrt in platforms_list:
if ("cpp" == context["language"]):
context["src_project_name"] = "HelloCpp"
context["src_package_name"] = "org.cocos2dx.hellocpp"
context["src_project_path"] = os.getcwd() + "/../../template/multi-platform-cpp"
platforms_list = ["ios",
"android",
"win32",
"winrt",
"wp8",
"mac",
"blackberry",
"linux",
"marmalade"]
Then it uses those values here:
for platform in platforms_list:
processPlatformProjects(platform)
Project template is supposed to be created in your project path.
cocos2d-x 3.0 doesnt support winrt or windows phone. You would have to use 2.2.3 for it and cpp as the language.
For winrt you would use the proj.winrt folder and proj.wp8 or proj.wp8-XAML for making games for wp8.