diff --git a/lib/motion/project/template/android.rb b/lib/motion/project/template/android.rb index 5ff9cc45..c0c02ba2 100644 --- a/lib/motion/project/template/android.rb +++ b/lib/motion/project/template/android.rb @@ -2,16 +2,16 @@ # Copyright (c) 2012, HipByte SPRL and contributors # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: -# +# # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. -# +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -38,13 +38,9 @@ mkdir_p app_build_dir # Generate the Android manifest file. - android_manifest_txt = '' - android_manifest_txt << < - - -EOS - # Application permissions. + android_manifest_txt = "\n" + + # permissions permissions = Array(App.config.permissions) if App.config.development? # In development mode, we need the INTERNET permission in order to create @@ -53,51 +49,37 @@ end permissions.each do |permission| permission = "android.permission.#{permission.to_s.upcase}" if permission.is_a?(Symbol) - android_manifest_txt << < -EOS + App.config.manifest.child('application').add_child('uses-permission', 'android:name' => "#{permission}") end - # Application features. - features = Array(App.config.features) - features.each do |feature| - android_manifest_txt << < -EOS + + # features + App.config.features.each do |feature| + App.config.manifest.child('application').add_child('uses-feature', 'android:name' => "#{feature}") end - # Custom manifest entries. - App.config.manifest_xml_lines(nil).each { |line| android_manifest_txt << "\t" + line + "\n" } - android_manifest_txt << < -EOS - App.config.manifest_xml_lines('application').each { |line| android_manifest_txt << "\t\t" + line + "\n" } - # Main activity. - android_manifest_txt << < - - - - - -EOS - # Sub-activities. + + # sub activities (App.config.sub_activities.uniq - [App.config.main_activity]).each do |activity| - android_manifest_txt << < - - -EOS + + App.config.manifest.child('application').add_child('activity') do |sub_activity| + + sub_activity['android:name'] = "#{activity}" + sub_activity['android:label'] = "#{activity}" + sub_activity['android:parentActivityName'] = -> { "#{App.config.main_activity}" } + + sub_activity.add_child('meta-data') do |meta| + meta['android:name'] = 'android.support.PARENT_ACTIVITY' + meta['android:value'] = -> { "#{App.config.main_activity}" } + end + end end - # Services. - services = Array(App.config.services) - services.each do |service| - android_manifest_txt << < -EOS + + # services + App.config.services.each do |service| + App.config.manifest.child('application').add_child('service', 'android:name' => "#{service}", 'android:exported' => 'false') end - android_manifest_txt << < - -EOS + + android_manifest_txt << App.config.manifest.to_xml + android_manifest = File.join(app_build_dir, 'AndroidManifest.xml') if !File.exist?(android_manifest) or File.read(android_manifest) != android_manifest_txt App.info 'Create', android_manifest @@ -323,7 +305,7 @@ else # Probably something else (what could it be?). add_method = true - end + end end current_class[:methods] << method_line if add_method else diff --git a/lib/motion/project/template/android/config.rb b/lib/motion/project/template/android/config.rb index b2ea3a09..39c40df6 100644 --- a/lib/motion/project/template/android/config.rb +++ b/lib/motion/project/template/android/config.rb @@ -2,16 +2,16 @@ # Copyright (c) 2012, HipByte SPRL and contributors # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: -# +# # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. -# +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,13 +24,71 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. module Motion; module Project; + + class AndroidManifest < Hash + + attr_reader :name + + def initialize(name = 'manifest') + @name = name + @children = [] + end + + def add_child(name, properties = {}, &block) + nested = AndroidManifest.new(name) + nested.merge!(properties) + block.call(nested) if block + @children << nested + end + + def child(name) + children(name).first + end + + def children(name) + @children.select { |c| c.name == name } + end + + def to_xml(depth = 0) + str = "#{' ' * depth}<#{@name} " + + str << map { |key, value| "#{key}=\"#{evaluate(value)}\"" }.join(' ') + + if @children.empty? + str << " />\n" + else + str << " >\n" + + # children + str << @children.map { |c| c.to_xml(depth + 1) }.join('') + + xml_lines_name = @name == "manifest" ? nil : @name + str << App.config.manifest_xml_lines(xml_lines_name).map { |line| "#{' ' * (depth + 1) }#{line}\n" }.join('') + + str << "#{' ' * depth}\n" + end + str + end + + private + + def evaluate(value) + if value.is_a? Proc + value.call + else + value + end + end + + end + class AndroidConfig < Config register :android variable :sdk_path, :ndk_path, :avd_config, :package, :main_activity, :sub_activities, :api_version, :target_api_version, :arch, :assets_dirs, :icon, :logs_components, :version_code, :version_name, :permissions, - :features, :services, :application_class + :features, :services, :application_class, :manifest def initialize(project_dir, build_mode) super @@ -50,6 +108,9 @@ def initialize(project_dir, build_mode) @version_name = '1.0' @application_class = nil + @manifest = AndroidManifest.new + construct_manifest + if path = ENV['RUBYMOTION_ANDROID_SDK'] @sdk_path = File.expand_path(path) end @@ -58,6 +119,36 @@ def initialize(project_dir, build_mode) end end + def construct_manifest + manifest = @manifest + + manifest['xmlns:android'] = 'http://schemas.android.com/apk/res/android' + manifest['package'] = -> { package } + + manifest['android:versionCode'] = -> { "#{version_code}" } + manifest['android:versionName'] = -> { "#{version_name}" } + + manifest.add_child('uses-sdk') do |uses_sdk| + uses_sdk['android:minSdkVersion'] = -> { "#{api_version}" } + uses_sdk['android:targetSdkVersion'] = -> { "#{target_api_version}" } + end + + manifest.add_child('application') do |application| + application['android:label'] = -> { "#{name}" } + application['android:debuggable'] = -> { "#{development? ? 'true' : 'false'}" } + application['android:icon'] = -> { icon ? "@drawable/#{icon}" : nil } + application['android:name'] = -> { application_class ? application_class : nil } + application.add_child('activity') do |activity| + activity['android:name'] = -> { main_activity } + activity['android:label'] = -> { name } + activity.add_child('intent-filter') do |filter| + filter.add_child('action', 'android:name' => 'android.intent.action.MAIN' ) + filter.add_child('category', 'android:name' => 'android.intent.category.LAUNCHER' ) + end + end + end + end + def validate if !sdk_path or !File.exist?(sdk_path) App.fail "app.sdk_path should point to a valid Android SDK directory." @@ -68,7 +159,7 @@ def validate end if api_version == nil or !File.exist?("#{sdk_path}//platforms/android-#{api_version}") - App.fail "The Android SDK installed on your system does not support " + (api_version == nil ? "any API level" : "API level #{api_version}") + ". Run the `#{sdk_path}/tools/android' program to install missing API levels." + App.fail "The Android SDK installed on your system does not support " + (api_version == nil ? "any API level" : "API level #{api_version}") + ". Run the `#{sdk_path}/tools/android' program to install missing API levels." end if !File.exist?("#{ndk_path}/platforms/android-#{api_version_ndk}") @@ -234,7 +325,7 @@ def armeabi_directory_name 'armeabi-v7a' else raise "Invalid arch `#{arch}'" - end + end end def bin_exec(name)