摘要:在制作一款打卡的時候有這么一個需求創建提醒到系統日歷中,也就是利用系統日歷來做事件的提醒,這么做的好處很明顯,無需處理提醒的細節,也無需后臺。
在制作一款打卡App的時候有這么一個需求 -- 創建提醒到系統日歷中,也就是利用系統日歷來做事件的提醒,這么做的好處很明顯,App無需處理提醒的細節,也無需后臺。這個App是基于Cordova開發的,并沒有訪問系統日歷的接口,我們只能通過插件來完成,這是一個有趣的挑戰。
APP設計請看下圖,App允許創建事項時,可以設置重復,開始,結束和提醒時間四個選項:
這四個選項對創建到日歷的事件都有影響:
重復 可以設置一周中的任意幾天,比如選擇周一到周五表示只要工作日。
開始 從哪天天開始
結束 到哪天結束
提醒時間 在每天的哪個時間發出提醒
這個四個組合起來就構成一個日歷事件。
插件Cordova平臺實際上是一個基于web的平臺,所以除了webview提供的能力,其他和設備交互的功能全部依賴于插件來完成,插件的安裝和使用通常并不困難,比如增加一個關于狀態欄控制的插件,可以在項目下這么做:
cordova plugin add cordova-plugin-statusbar
然后在js里調用插件提供的接口就可以了,更多的關于cordova平臺和插件的使用,我有一個視頻課程可以參考。很明顯,創建系統日歷事件也需要通過插件來做,搜索之后我們可以發現,完成這個功能的插件并不多,其中使用比較多的是cordova-plugin-calendar,試著安裝
cordova plugin add cordova-plugin-calendar
這個插件可以支持android和iOS,我們現在android下測試,首先在js里寫下下面的代碼:
let calOptions = window.plugins.calendar.getCalendarOptions() calOptions.firstReminderMinutes = 0 calOptions.secondReminderMinutes = null calOptions.recurrenceEndDate = actionRemindEnd(action) if (action.repeat.length === 7) { calOptions.recurrence = "daily" } else { calOptions.recurrence = "weekly" calOptions.recurrenceByDay = dateRepeat2Calendar(action.repeat) } window.plugins.calendar.createEventWithOptions( action.name, null, "dayday", eventTime, new Date(eventTime.getTime() + 15 * 60000), calOptions, (result) => { }, (err) => { })
action保存了用戶所創建的一個活動事件的所有信息,其中有兩個函數就不展開了,看起來應該可以了,實測的結果卻是,日歷事件創建起來了,沒有報錯,但是重復有問題,并沒有能一直重復下去,在重復數次之后,事件就停了,類似下圖這樣,到15號事件就沒有了:
修改插件在這種情況下,調試js代碼已經沒有什么幫助了,js代碼已經完全按照插件的要求來傳遞了參數,只能打開android studio,加載cordova項目下platforms/android下的這個工程,這個工程就是一個標準的android項目,打開之后可以定位到這個插件所提供的源碼文件,找到AbstractCalendarAccessor.java,其中的createEvent函數完成在android下創建一個日歷事件所做的事情,代碼如下:
public String createEvent(Uri eventsUri, String title, long startTime, long endTime, String description, String location, Long firstReminderMinutes, Long secondReminderMinutes, String recurrence, int recurrenceInterval, String recurrenceWeekstart, String recurrenceByDay, String recurrenceByMonthDay, Long recurrenceEndTime, Long recurrenceCount, String allday, Integer calendarId, String url) { ContentResolver cr = this.cordova.getActivity().getContentResolver(); ContentValues values = new ContentValues(); final boolean allDayEvent = "true".equals(allday) && isAllDayEvent(new Date(startTime), new Date(endTime)); if (allDayEvent) { //all day events must be in UTC time zone per Android specification, getOffset accounts for daylight savings time values.put(Events.EVENT_TIMEZONE, "UTC"); values.put(Events.DTSTART, startTime + TimeZone.getDefault().getOffset(startTime)); values.put(Events.DTEND, endTime + TimeZone.getDefault().getOffset(endTime)); } else { values.put(Events.EVENT_TIMEZONE, TimeZone.getDefault().getID()); values.put(Events.DTSTART, startTime); values.put(Events.DTEND, endTime); } values.put(Events.ALL_DAY, allDayEvent ? 1 : 0); values.put(Events.TITLE, title); // there"s no separate url field, so adding it to the notes if (url != null) { if (description == null) { description = url; } else { description += " " + url; } } values.put(Events.DESCRIPTION, description); values.put(Events.HAS_ALARM, firstReminderMinutes > -1 || secondReminderMinutes > -1 ? 1 : 0); values.put(Events.CALENDAR_ID, calendarId); values.put(Events.EVENT_LOCATION, location); if (recurrence != null) { String rrule = "FREQ=" + recurrence.toUpperCase() + ((recurrenceInterval > -1) ? ";INTERVAL=" + recurrenceInterval : "") + ((recurrenceWeekstart != null) ? ";WKST=" + recurrenceWeekstart : "") + ((recurrenceByDay != null) ? ";BYDAY=" + recurrenceByDay : "") + ((recurrenceByMonthDay != null) ? ";BYMONTHDAY=" + recurrenceByMonthDay : "") + ((recurrenceEndTime > -1) ? ";UNTIL=" + nl.xservices.plugins.Calendar.formatICalDateTime(new Date(recurrenceEndTime)) : "") + ((recurrenceCount > -1) ? ";COUNT=" + recurrenceCount : ""); values.put(Events.RRULE, rrule); } String createdEventID = null; try { Uri uri = cr.insert(eventsUri, values); createdEventID = uri.getLastPathSegment(); Log.d(LOG_TAG, "Created event with ID " + createdEventID); if (firstReminderMinutes > -1) { ContentValues reminderValues = new ContentValues(); reminderValues.put("event_id", Long.parseLong(uri.getLastPathSegment())); reminderValues.put("minutes", firstReminderMinutes); reminderValues.put("method", 1); cr.insert(Uri.parse(CONTENT_PROVIDER + CONTENT_PROVIDER_PATH_REMINDERS), reminderValues); } if (secondReminderMinutes > -1) { ContentValues reminderValues = new ContentValues(); reminderValues.put("event_id", Long.parseLong(uri.getLastPathSegment())); reminderValues.put("minutes", secondReminderMinutes); reminderValues.put("method", 1); cr.insert(Uri.parse(CONTENT_PROVIDER + CONTENT_PROVIDER_PATH_REMINDERS), reminderValues); } } catch (Exception e) { Log.e(LOG_TAG, "Creating reminders failed, ignoring since the event was created.", e); } return createdEventID; }
這段代碼并不長,在Android Studio下設置斷點,連接真機調試,發現整個過程沒有任何錯誤,日歷事件已經創建起來,但就是重復次數不正確。好吧,找到android api參考,看看官方文檔中怎么說的:
Here are the rules for inserting a new event:
You must include CALENDAR_ID and DTSTART.
You must include an EVENT_TIMEZONE. To get a list of the system"s installed time zone IDs, use getAvailableIDs(). Note that this rule does not apply if you"re inserting an event through the INSERT Intent, described in Using an intent to insert an event—in that scenario, a default time zone is supplied.
For non-recurring events, you must include DTEND.
For recurring events, you must include a DURATION in addition to RRULE or RDATE. Note that this rule does not apply if you"re inserting an event through the INSERT Intent, described in Using an intent to insert an event—in that scenario, you can use an RRULE in conjunction with DTSTART and DTEND, and the Calendar application converts it to a duration automatically.
仔細對照代碼和文檔,我們發現DURATION這個參數并沒有按照文檔來傳遞,好吧,我們修改一下關鍵代碼:
if (allDayEvent) { //all day events must be in UTC time zone per Android specification, getOffset accounts for daylight savings time values.put(Events.EVENT_TIMEZONE, "UTC"); values.put(Events.DTSTART, startTime + TimeZone.getDefault().getOffset(startTime)); if (recurrence == null) { values.put(Events.DTEND, endTime + TimeZone.getDefault().getOffset(endTime)); } else { values.put(Events.DURATION, "P" + ((endTime - startTime) / (24 * 60 * 60000)) + "D"); } } else { values.put(Events.EVENT_TIMEZONE, TimeZone.getDefault().getID()); values.put(Events.DTSTART, startTime); if (recurrence == null) { values.put(Events.DTEND, endTime); } else { values.put(Events.DURATION, "P" + ((endTime - startTime) / 60000) + "M"); } }
修改后的代碼再次測試,這次ok了,這個例子表明了cordova生態的一個現象,插件質量參差不齊,有些插件可能需要我們的修改才能工作。
應用修改為了使用修改后的插件,我們可以刪除原來的插件,使用fork并修改后的插件,很簡單,方法如下:
cordova plugin remove cordova-plugin-calendar cordova plugin add https://github.com/i38/Calendar-PhoneGap-Plugin.git
所有其他代碼都不用修改,這是cordova很靈活的一個地方,這樣一切都ok了,最后附上完整App的鏈接,有興趣可以參考: 天天。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99841.html
摘要:本文源碼為版本。的代碼結構也是一個很經典的定義結構構造函數實例修改函數原型共享實例方法,它提供事件通道上事件的訂閱撤消訂閱調用。 前言 cordova(PhoneGap) 是一個優秀的經典的中間件框架,網上對其源代碼解讀的文章確實不多,本系列文章試著解讀一下,以便對cordova 框架的原理理解得更深入。本文源碼為cordova android版本6.1.2。 源碼結構 我們使用IDE...
摘要:經過網上查找很多資料,發現很多只有的項目整合,但是使用插件的文章很少,現在把從創建和創建到使用插件到項目打包到手機運行過程記錄下來先上項目結構目錄項目創建安裝環境這個這邊就不描述了,網上很多教程創建應用創建項目為目錄命名空間項目名稱添加平臺 經過網上查找很多資料,發現很多只有vue+cordova的項目整合,但是vue使用cordova插件的文章很少,現在把從創建cordova和創建v...
摘要:經過網上查找很多資料,發現很多只有的項目整合,但是使用插件的文章很少,現在把從創建和創建到使用插件到項目打包到手機運行過程記錄下來先上項目結構目錄項目創建安裝環境這個這邊就不描述了,網上很多教程創建應用創建項目為目錄命名空間項目名稱添加平臺 經過網上查找很多資料,發現很多只有vue+cordova的項目整合,但是vue使用cordova插件的文章很少,現在把從創建cordova和創建v...
摘要:任何初始化任務應該在文件中的事件的事件處理函數中。這個配置文件有幾個地方很關鍵,一開始沒有認真看,將插件導進工程跑的時候各種問題,十分頭痛,不得不重新認真看看文檔。 前言 來新公司的第一個任務,研究hybrid App中間層實現原理,做中間層插件開發。這個任務挺有意思,也很有挑戰性,之前在DCloud雖然做過5+ App開發,但是中間層的東西確實涉及不多。本系列文章屬于系列開篇cord...
閱讀 2804·2023-04-25 18:46
閱讀 696·2021-11-19 09:40
閱讀 2063·2021-09-28 09:36
閱讀 3374·2021-09-10 11:11
閱讀 3453·2019-08-30 15:55
閱讀 1791·2019-08-30 15:54
閱讀 2589·2019-08-29 16:16
閱讀 3536·2019-08-29 15:08