Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
506 views
in Technique[技术] by (71.8m points)

cordova - datePicker plugin not working in Phonegap 2.0

After upgrading to Phonegap 2.0, the datePicker plugin does not work. The error is: Uncaught TypeError: Cannot read property 'datePicker' of undefined.

the error occcurs on the javascript code: window.plugins.datePicker.show({...

The DatePicker js file:

/**
* Phonegap DatePicker Plugin Copyright (c) Greg Allen 2011 MIT Licensed
* Reused and ported to Android plugin by Daniel van 't Oever
*/
if (typeof cordova !== "undefined") {
/**
 * Constructor
 */
function DatePicker() {
    this._callback;
}

/**
 * show - true to show the ad, false to hide the ad
 */
DatePicker.prototype.show = function(options, cb) {
    if (options.date) {
        options.date = (options.date.getMonth() + 1) + "/" + (options.date.getDate()) + "/" + (options.date.getFullYear()) + "/"
                + (options.date.getHours()) + "/" + (options.date.getMinutes());
    }
    var defaults = {
        mode : '',
        date : '',
        allowOldDates : true
    };

    for ( var key in defaults) {
        if (typeof options[key] !== "undefined")
            defaults[key] = options[key];
    }
    this._callback = cb;

    return cordova.exec(cb, failureCallback, 'DatePickerPlugin', defaults.mode, new Array(defaults));
};

DatePicker.prototype._dateSelected = function(date) {
    var d = new Date(parseFloat(date) * 1000);
    if (this._callback)
        this._callback(d);
};

function failureCallback(err) {
    console.log("datePickerPlugin.js failed: " + err);
}

cordova.addConstructor(function() {debugger;
    if (!window.plugins) {
        window.plugins = {};
    }
    window.plugins.datePicker = new DatePicker();
});
};

The DatePicker plugin java file:

    /**
 * 
 */
package com.phonegap.plugin;

import java.util.Calendar;
import java.util.Date;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.util.Log;
import android.widget.DatePicker;
import android.widget.TimePicker;


import org.apache.cordova.DroidGap;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;

/**
 * @author ng4e
 * @author Daniel van 't Oever
 * 
 *         Rewrote plugin so it it similar to the iOS datepicker plugin and it
 *         accepts prefilled dates and time
 */
public class DatePickerPlugin extends Plugin {

    private static final String ACTION_DATE = "date";
    private static final String ACTION_TIME = "time";
    private final String pluginName = "DatePickerPlugin";

    /*
     * (non-Javadoc)
     * 
     * @see com.phonegap.api.Plugin#execute(java.lang.String,
     * org.json.JSONArray, java.lang.String)
     */
    @Override
    public PluginResult execute(final String action, final JSONArray data, final String callBackId) {
        Log.d(pluginName, "DatePicker called with options: " + data);
        PluginResult result = null;

        this.show(data, callBackId);
        result = new PluginResult(PluginResult.Status.NO_RESULT);
        result.setKeepCallback(true);

        return result;
    }

    public synchronized void show(final JSONArray data, final String callBackId) {
        final DatePickerPlugin datePickerPlugin = this;
        @SuppressWarnings("deprecation")
        final DroidGap currentCtx = (DroidGap) ctx.getContext();
        final Calendar c = Calendar.getInstance();
        final Runnable runnable;

        String action = "date";

        /*
         * Parse information from data parameter and where possible, override
         * above date fields
         */
        int month = -1, day = -1, year = -1, hour = -1, min = -1;
        try {
            JSONObject obj = data.getJSONObject(0);
            action = obj.getString("mode");

            String optionDate = obj.getString("date");

            String[] datePart = optionDate.split("/");
            month = Integer.parseInt(datePart[0]);
            day = Integer.parseInt(datePart[1]);
            year = Integer.parseInt(datePart[2]);
            hour = Integer.parseInt(datePart[3]);
            min = Integer.parseInt(datePart[4]);

            /* currently not handled in Android */
            // boolean optionAllowOldDates = obj.getBoolean("allowOldDates");

        } catch (JSONException e) {
            e.printStackTrace();
        }

        // By default initialize these fields to 'now'
        final int mYear = year == -1 ? c.get(Calendar.YEAR) : year;
        final int mMonth = month == -1 ? c.get(Calendar.MONTH) : month - 1;
        final int mDay = day == -1 ? c.get(Calendar.DAY_OF_MONTH) : day;
        final int mHour = hour == -1 ? c.get(Calendar.HOUR_OF_DAY) : hour;
        final int mMinutes = min == -1 ? c.get(Calendar.MINUTE) : min;

        if (ACTION_TIME.equalsIgnoreCase(action)) {
            runnable = new Runnable() {
                public void run() {
                    final TimeSetListener timeSetListener = new TimeSetListener(datePickerPlugin, callBackId);
                    final TimePickerDialog timeDialog = new TimePickerDialog(currentCtx, timeSetListener, mHour,
                            mMinutes, true);
                    timeDialog.show();
                }
            };

        } else if (ACTION_DATE.equalsIgnoreCase(action)) {
            runnable = new Runnable() {
                public void run() {
                    final DateSetListener dateSetListener = new DateSetListener(datePickerPlugin, callBackId);
                    final DatePickerDialog dateDialog = new DatePickerDialog(currentCtx, dateSetListener, mYear,
                            mMonth, mDay);
                    dateDialog.show();
                }
            };

        } else {
            Log.d(pluginName, "Unknown action. Only 'date' or 'time' are valid actions");
            return;
        }

        //((Activity) ctx).runOnUiThread(runnable);
    }

    private final class DateSetListener implements OnDateSetListener {
        private final DatePickerPlugin datePickerPlugin;
        private final String callBackId;

        private DateSetListener(DatePickerPlugin datePickerPlugin, String callBackId) {
            this.datePickerPlugin = datePickerPlugin;
            this.callBackId = callBackId;
        }

        /**
         * Return a string containing the date in the format YYYY/MM/DD
         */
        public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth) {
            String returnDate = year + "/" + (monthOfYear + 1) + "/" + dayOfMonth;
            datePickerPlugin.success(new PluginResult(PluginResult.Status.OK, returnDate), callBackId);

        }
    }

    private final class TimeSetListener implements OnTimeSetListener {
        private final DatePickerPlugin datePickerPlugin;
        private final String callBackId;

        private TimeSetListener(DatePickerPlugin datePickerPlugin, String callBackId) {
            this.datePickerPlugin = datePickerPlugin;
            this.callBackId = callBackId;
        }

        /**
         * Return the current date with the time modified as it was set in the
         * time picker.
         */
        public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
            /*Date date = new Date();
            date.setHours(hourOfDay);
            date.setMinutes(minute);*/
            Calendar today = Calendar.getInstance();
            today.set(Calendar.HOUR_OF_DAY, hourOfDay);
            today.set(Calendar.MINUTE, minute);
            Date date = today.getTime();

            datePickerPlugin.success(new PluginResult(PluginResult.Status.OK, date.toString()), callBackId);

        }
    }

}

After I spend time on google, I made the following changes: replace

    cordova.addConstructor(function() {debugger;
    if (!window.plugins) {
        window.plugins = {};
    }
    window.plugins.datePicker = new DatePicker();
    });

with

window.datePicker = new DatePicker();

Update the js code of calling it to

window.datePicker.show({...

Now I can get the datePicker object anyway, but got this new error:

Uncaught TypeError: Object # has no method 'exec'

on

DatePicker.prototype.show = function(options, cb) {
    ...
    return cordova.exec(cb, failureCallback, 'DatePickerPlugin', defaults.mode, new  Array(defaults));
})

Thanks for any help!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

http://docs.phonegap.com/en/2.0.0/guide_plugin-development_android_index.md.html#Developing%20a%20Plugin%20on%20Android

After checking this, i found:

Common Pitfalls

Plugins have access to a CordovaInterface object. This object has access to the Android Activity that is running the application. This is the Context required to launch a new Android Intent. The CordovaInterface allows plugins to start an Activity for a result, and to set the callback plugin for when the Intent comes back to the application. This is important, since the Intents system is how Android communicates between processes.

Plugins do not have direct access to the Context as they have in the past. The legacy ctx member is deprecated, and will be removed six months after 2.0 is released. All the methods that ctx has exist on the Context, so both getContext() and getActivity() are capable of returning the proper object required.

Avoid calling JavaScript using webView.loadUrl(). The reason we have a callback server is to allow JavaScript execution to be thread-safe, and loadUrl explicitly interrupts the UI thread, and can affect the usability of your plugin.

Here is my fix:

in DatePickerPlugin.java

import android.content.Context;
....

public synchronized void show(final JSONArray data, final String callBackId) {
     final DatePickerPlugin datePickerPlugin = this;
     final DroidGap currentCtx = (DroidGap) ctx.getContext();
     final Calendar c = Calendar.getInstance();
.....

replace line:

final DroidGap currentCtx = (DroidGap) ctx.getContext();

by:

final Context currentCtx = cordova.getActivity();

Find:

ctx.runOnUiThread(runnable);

replace by:

cordova.getActivity().runOnUiThread(runnable);

This is working fine in my emulator 4.0.3 with phonegap 2.0


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...