Cordova插件Android Activity - 访问资源

时间:2014-09-30 09:12:00

标签: android cordova

我正在开发Android的Cordova插件,我很难克服从活动中访问项目资源 - 插件应该是项目独立的,但访问资源(例如R.java)证明是棘手的。

我的插件目前由两个非常简单的类组成:RedLaser.javaRedLaserScanner.java

RedLaser.java

继承自CordovaPlugin,因此包含execute方法,其外观类似于以下内容。

public class RedLaser extends CordovaPlugin {
    private static final string SCAN_ACTION = "scan";

    public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
        if (action.equals(SCAN_ACTION)) {
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    scan(args, callbackContext);
                }
            });

            return true;
        }

        return false;
    }

    private void scan(JSONArray args, CallbackContext callbackContext) {
        Intent intent = new Intent(this.cordova.getActivity().getApplicationContext(), RedLaserScanner.class);
        this.cordova.startActivityForResult((CordovaPlugin) this, intent, 1);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Do something with the result
    }
}

RedLaserScanner.java

RedLaserScanner包含Android Activity逻辑,并继承自BarcodeScanActivity(这是一个RedLaser SDK类,可能是继承自Activity);

一个非常简单的结构如下:

public class RedLaserScanner extends BarcodeScanActivity {
    @Override
    public void onCreate(Bundle icicle) {               
        super.onCreate(icicle);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.preview_overlay_new_portrait);
    }
}

我遇到了麻烦,因为我需要访问项目的资源来访问R.layout.preview_overlay_new_portrait(它们分散在Eclipse项目中) - 但我不能这样做,除非我导入com.myProject.myApp.R - 这使得我的插件依赖于项目本身。

我做了一些调查,发现cordova.getActivity().getResources()似乎很有用,但是我的RedLaserScanner无法访问它 - 因为它不会从CordovaPlugin继承。

有人可以帮我指点一下吗?

由于

4 个答案:

答案 0 :(得分:7)

我刚遇到同样的问题,结果很容易解决。 RedLaserScanner扩展了一个活动,所以你可以像这样调用getResources():

setContentView(getResources("preview_overlay_new_portrait", "layout", getPackageName()));

答案 1 :(得分:3)

Hooks可用于替换源文件内容以删除错误的导入和/或添加正确的资源导入。

我创建了一个无需指定文件的脚本。它尝试查找源文件(使用 .java 扩展名),删除其中已有的任何资源导入,然后使用Cordova应用程序包名称导入正确的资源(如果需要)。

这是剧本:

#!/usr/bin/env node

/*
 * A hook to add resources class (R.java) import to Android classes which uses it.
 */

function getRegexGroupMatches(string, regex, index) {
    index || (index = 1)

    var matches = [];
    var match;
    if (regex.global) {
        while (match = regex.exec(string)) {
            matches.push(match[index]);
            console.log('Match:', match);
        }
    }
    else {
        if (match = regex.exec(string)) {
            matches.push(match[index]);
        }
    }

    return matches;
}

module.exports = function (ctx) {
    // If Android platform is not installed, don't even execute
    if (ctx.opts.cordova.platforms.indexOf('android') < 0)
        return;

    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path'),
        Q = ctx.requireCordovaModule('q');

    var deferral = Q.defer();

    var platformSourcesRoot = path.join(ctx.opts.projectRoot, 'platforms/android/src');
    var pluginSourcesRoot = path.join(ctx.opts.plugin.dir, 'src/android');

    var androidPluginsData = JSON.parse(fs.readFileSync(path.join(ctx.opts.projectRoot, 'plugins', 'android.json'), 'utf8'));
    var appPackage = androidPluginsData.installed_plugins[ctx.opts.plugin.id]['PACKAGE_NAME'];

    fs.readdir(pluginSourcesRoot, function (err, files) {
        if (err) {
            console.error('Error when reading file:', err)
            deferral.reject();
            return
        }

        var deferrals = [];

        files.filter(function (file) { return path.extname(file) === '.java'; })
            .forEach(function (file) {
                var deferral = Q.defer();

                var filename = path.basename(file);
                var file = path.join(pluginSourcesRoot, filename);
                fs.readFile(file, 'utf-8', function (err, contents) {
                    if (err) {
                        console.error('Error when reading file:', err)
                        deferral.reject();
                        return
                    }

                    if (contents.match(/[^\.\w]R\./)) {
                        console.log('Trying to get packages from file:', filename);
                        var packages = getRegexGroupMatches(contents, /package ([^;]+);/);
                        for (var p = 0; p < packages.length; p++) {
                            try {
                                var package = packages[p];

                                var sourceFile = path.join(platformSourcesRoot, package.replace(/\./g, '/'), filename)
                                if (!fs.existsSync(sourceFile)) 
                                    throw 'Can\'t find file in installed platform directory: "' + sourceFile + '".';

                                var sourceFileContents = fs.readFileSync(sourceFile, 'utf8');
                                if (!sourceFileContents) 
                                    throw 'Can\'t read file contents.';

                                var newContents = sourceFileContents
                                    .replace(/(import ([^;]+).R;)/g, '')
                                    .replace(/(package ([^;]+);)/g, '$1 import ' + appPackage + '.R;');

                                fs.writeFileSync(sourceFile, newContents, 'utf8');
                                break;
                            }
                            catch (ex) {
                                console.log('Could not add import to "' +  filename + '" using package "' + package + '". ' + ex);
                            }
                        }
                    }
                });

                deferrals.push(deferral.promise);
            });

        Q.all(deferrals)
            .then(function() {
                console.log('Done with the hook!');
                deferral.resolve();
            })
    });

    return deferral.promise;
}

只需在 plugin.xml 中添加 after_plugin_install 挂钩(适用于Android平台):

<hook type="after_plugin_install" src="scripts/android/addResourcesClassImport.js" />

希望它有所帮助!

答案 2 :(得分:1)

我为此实施了一个帮助程序以保持清洁。当您创建一个插件时,它也会有所帮助,该插件接受存储在插件中的字符串资源文件中的config.xml参数。

private int getAppResource(String name, String type) {
    return cordova.getActivity().getResources().getIdentifier(name, type, cordova.getActivity().getPackageName());
}

您可以按如下方式使用它:

getAppResource("app_name", "string");

这将返回app_name的字符串资源ID,实际值仍然需要通过调用:

来检索
this.activity.getString(getAppResource("app_name", "string"))

或者原始问题中的情况:

setContentView(getAppResource("preview_overlay_new_portrait", "layout"));

这些天我只是创建一个帮助程序,它直接从帮助程序返回值:

private String getStringResource(String name) {
    return this.activity.getString(
        this.activity.getResources().getIdentifier(
          name, "string", this.activity.getPackageName()));
}

反过来你会这样打电话:

this.getStringResource("app_name");

我认为重要的是要指出,当你拥有资源ID时,你并不总是存在。

答案 3 :(得分:-1)

尝试使用android.R.layout.preview_overlay_new_portrait