简体   繁体   English

Cordova插件Android Activity - 访问资源

[英]Cordova plugin Android Activity - accessing resources

I am developing a Cordova plugin for Android and I am having difficulty overcoming accessing project resources from within an activity - the plugin should be project independent, but accessing the resources (eg R.java) is proving tricky. 我正在开发Android的Cordova插件,我很难克服从活动中访问项目资源 - 插件应该是项目独立的,但访问资源(例如R.java)证明是棘手的。

My plugin, for now, is made up of two very simple classes: RedLaser.java and RedLaserScanner.java . 我的插件目前由两个非常简单的类组成: RedLaser.javaRedLaserScanner.java

RedLaser.java

Inherits from CordovaPlugin and so contains the execute method and looks similar to the following. 继承自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

The RedLaserScanner contains the Android Activity logic and inherits from BarcodeScanActivity (which is a RedLaser SDK class, presumably itself inherits from Activity); RedLaserScanner包含Android Activity逻辑,并继承自BarcodeScanActivity(这是一个RedLaser SDK类,可能本身继承自Activity);

A very simple structure is as follows: 一个非常简单的结构如下:

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);
    }
}

I am having trouble because I need to access the project's resources to access R.layout.preview_overlay_new_portrait (which are scatted in the Eclipse project) - but I cannot do this unless I import com.myProject.myApp.R - which makes my plugin have a dependency on the project itself. 我遇到了麻烦,因为我需要访问项目的资源来访问R.layout.preview_overlay_new_portrait (它们分散在Eclipse项目中) - 但我不能这样做,除非我导入com.myProject.myApp.R - 这使得我的插件有对项目本身的依赖。

I did some investigation and found cordova.getActivity().getResources() which seems useful, but this is not accessible from within my RedLaserScanner - because it does not inherit from CordovaPlugin. 我做了一些调查,发现cordova.getActivity().getResources()似乎很有用,但是我无法从RedLaserScanner中访问 - 因为它不会从CordovaPlugin继承。

Can somebody please help me with some pointers? 有人可以帮我指点一下吗?

Thanks 谢谢

I just ran into the same issue and it turns out to be pretty easy to solve. 我刚遇到同样的问题,结果很容易解决。 RedLaserScanner extends an activity, so you can just call getResources() like this: RedLaserScanner扩展了一个活动,所以你可以像这样调用getResources():

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

Hooks can be used to replace source file contents to remove wrong imports and/or add the right imports of resources. 钩子可用于替换源文件内容以删除错误的导入和/或添加正确的资源导入。

I created a script that do it without needing to specify the files. 我创建了一个脚本,无需指定文件即可完成。 It tries to find source files (with .java extension), removes any resource import already in it and then put the right resources import (if needed), using the Cordova application package name. 它尝试查找源文件(扩展名为.java ),删除其中已有的任何资源导入,然后使用Cordova应用程序包名称导入正确的资源导入(如果需要)。

This is the script: 这是脚本:

#!/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;
}

Just add as an after_plugin_install hook (for Android platform) in your plugin.xml : 只需在plugin.xml中添加为after_plugin_install挂钩(适用于Android平台):

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

Hope it helps someone! 希望它可以帮到某人!

I implemented a helper for this to keep things clean. 我为此实现了一个帮助,以保持干净。 It also helps when you create a plugin which takes config.xml arguments which you store in a string resource file in the plugin. 当您创建一个插件时,它也会有所帮助,该插件接受存储在插件中的字符串资源文件中的config.xml参数。

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

You can use it as follows: 您可以按如下方式使用它:

getAppResource("app_name", "string");

That would return the string resource ID for app_name, the actually value still needs to be retrieved by calling: 这将返回app_name的字符串资源ID,实际值仍需要通过调用来检索:

this.activity.getString(getAppResource("app_name", "string"))

Or for the situation in the original question: 或者对于原始问题中的情况:

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

These days I just create a helper which returns the value immediately from the the helper: 这些天我只是创建一个帮助程序,它直接从帮助程序返回值:

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

which in turn you'd call like this: 反过来你会这样打电话:

this.getStringResource("app_name");

I think it's important to point out that when you have the resource ID you're not always there yet. 我认为重要的是要指出,当你拥有资源ID时,你并不总是在那里。

尝试使用android.R.layout.preview_overlay_new_portrait

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM