草庐IT

Android Calendar 系统日历提醒、日程同步系统

翻滚的咸鱼 2023-03-28 原文

安卓往系统中添加日程提醒,吭比较多。

首先有个需求(仿制 ios 日历),为什么仿制ios呢?这个得问产品了,我只是一个搬砖的程序员 (*´艸`) 捂嘴

大致有日期,时间,重复设置,自定义重复设置,位置提醒设置

跟系统日历的设置类似,毕竟需要同步到系统,所以设置上面保持规范,都是设置好日期时间,然后重复项。

一般的日历添加也比较简单(重复规则比较烦),先看效果图

添加日历首先得有一个账户,这个自己定义一个就行了

/**
     * 添加日历账户,账户创建成功则返回账户id,否则返回-1
     */
    private fun addCalendarAccount(context: Context): Long {
        val timeZone: TimeZone = TimeZone.getDefault()
        val value = ContentValues()
        value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME)
        value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME)
        value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
        value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME)
        value.put(CalendarContract.Calendars.VISIBLE, 1)//设置日历可见
        value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE)
        //使用的权限等级
        value.put(
            CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL,
            CalendarContract.Calendars.CAL_ACCESS_OWNER
        )
        value.put(CalendarContract.Calendars.SYNC_EVENTS, 1)//同步到系统
        value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID())
        value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME)
        value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0)
        var calendarUri = Uri.parse(CALENDER_URL)
        calendarUri = calendarUri.buildUpon()
            .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME)
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE)
            .build()
        val result = context.contentResolver.insert(calendarUri, value)
        log("addCalendarAccount result $result")
        return if (result == null) -1 else ContentUris.parseId(result)
    }

后面开始常规的日历添加操作,在UI上选择好时间,调用系统方法,同步到系统日历

/**
     * 添加日历事件
     */
    private fun addCalendarEvent(
        context: Context?,
        reminderTitle: String?,
        description: String?,
        reminderTime: Long,
        rule: String? = null,
    ): Boolean {
        log("addCalendarEvent $rule")
        if (context == null) {
            return false
        }
        val calId = checkAndAddCalendarAccount(context) //获取日历账户的id
        log("addCalendarEvent calId $calId")
        if (calId < 0) { //获取账户id失败直接返回,添加日历事件失败
            return false
        }
        deleteCalendarEvent(context, reminderTitle, description)
        //添加日历事件
        val mCalendar = Calendar.getInstance()
        mCalendar.timeInMillis = reminderTime //设置开始时间
        val start = mCalendar.time.time
        val event = ContentValues()
        event.put(CalendarContract.Events.TITLE, reminderTitle)
        event.put(CalendarContract.Events.DESCRIPTION, description)
        event.put(CalendarContract.Events.CALENDAR_ID, calId) //插入账户的id
        event.put(CalendarContract.Events.DTSTART, start)//开始时间
        //结束时间 ,如果事件是每天/周,那么就没有结束时间
        event.put(CalendarContract.Events.DTEND, start)
        event.put(CalendarContract.Events.HAS_ALARM, 1) //设置有闹钟提醒
        event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id) //时区
        if (rule != null) {
            event.put(CalendarContract.Events.RRULE, rule)
        }
        val calendarEvent = context.contentResolver.insert(Uri.parse(CALENDER_EVENT_URL), event)
            ?: return false
        log("addCalendarEvent newEvent $calendarEvent")
        //事件提醒的设定
        val reminders = ContentValues()
        reminders.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(calendarEvent))
        reminders.put(CalendarContract.Reminders.MINUTES, 0) // 提前几分钟提醒

        reminders.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT)//提醒次数
        val remindUri = context.contentResolver.insert(Uri.parse(CALENDER_REMINDER_URL), reminders)
        log("addCalendarEvent uri $remindUri")
        return remindUri != null
    }

很多设置其实都是固定值,或者系统规定配置,只需要传入一个日期时间

这样基本的添加其实已经完成,但是如果需要重复,自定义等操作,就复杂许多

重复提醒

首先重复提醒,也就是上图中的常规选择,也就是每小时,每天,每周每年等

在事件中添加重复规则

val rule = StringBuilder()
        when (repeatType) {
            //永不
            RemindRepeatType.NEVER.value -> {
                return null
            }
            //每小时
            RemindRepeatType.EVERY_HOUR.value -> {
                rule.append("FREQ=HOURLY;INTERVAL=1")
            }
            //每天
            RemindRepeatType.EVERY_DAY.value -> {
                rule.append("FREQ=DAILY;INTERVAL=1")
            }
            //工作日
            RemindRepeatType.EVERY_WORK_DAY.value -> {
                rule.append("FREQ=WEEKLY;INTERVAL=1")
                for (i in 0 until 5) {
                    if (i <= byDayArray.size) {
                        if (i == 0) {
                            rule.append(";BYDAY=${byDayArray[i]}")
                        } else {
                            rule.append(",${byDayArray[i]}")
                        }
                    }
                }
            }
            //周末
            RemindRepeatType.EVERY_WEEKEND.value -> {
                rule.append("FREQ=WEEKLY;INTERVAL=1;BYDAY=${byDayArray[5]},${byDayArray[6]}")
            }
            //每周
            RemindRepeatType.EVERY_WEEK.value -> {
                rule.append("FREQ=WEEKLY;INTERVAL=1")
            }
            //每两周
            RemindRepeatType.EVERY_TWO_WEEKS.value -> {
                rule.append("FREQ=WEEKLY;INTERVAL=2")
            }
            //每月
            RemindRepeatType.EVERY_MONTH.value -> {
                rule.append("FREQ=MONTHLY;INTERVAL=1")
            }
            //每3个月
            RemindRepeatType.EVERY_THREE_MONTHS.value -> {
                rule.append("FREQ=MONTHLY;INTERVAL=3")
            }
            //每6个月
            RemindRepeatType.EVERY_SIX_MONTHS.value -> {
                rule.append("FREQ=MONTHLY;INTERVAL=6")
            }
            //每年
            RemindRepeatType.EVERY_YEAR.value -> {
                rule.append("FREQ=YEARLY;INTERVAL=1")
            }
      
        }

规则都是通过字符自定义拼接,可读性比较差

 

FREQ :表示重复规则的类型, 必须定义

HOURLY:小时、DAILY:天、WEEKLY:周、MONTHLY:月、YEARLY:年

INTERVAL :重复间隔数

必须为正整数,默认值为1,表示每小时、每天

BYDAY : MO(周一),TU(周二),WE(周三),TU(周四),FR(周五),SA(周六),SU(周日)

比如每个周末重复:

FREQ=WEEKLY;INTERVAL=1;BYDAY=SA,SU

最后需要添加 ;WKST=SU ,表示从周几开始,硬性规定。

这只是常规的重复项,如果需要自定义重复项,也差距不大。

//1:日 2:周 3:月 4:年
                when (customRepeatFreq) {
                    1 -> rule.append("FREQ=DAILY;INTERVAL=$customRepeatInterval")
                    2 -> {
                        rule.append("FREQ=WEEKLY;INTERVAL=$customRepeatInterval")
                        customRepeatItems?.let {
                            for (i in it.indices) {
                                val index = it[i] - 1
                                if (index <= byDayArray.size) {
                                    if (i == 0) {
                                        rule.append(";BYDAY=${byDayArray[index]}")
                                    } else {
                                        rule.append(",${byDayArray[index]}")
                                    }
                                }
                            }
                        }
                    }
                    3 -> {
                        rule.append("FREQ=MONTHLY;INTERVAL=$customRepeatInterval")
                        customRepeatItems?.let {
                            for (i in it.indices) {
                                val index = it[i] - 1
                                if (index <= byMonthDayArray.size) {
                                    if (i == 0) {
                                        rule.append(";BYMONTHDAY=${byMonthDayArray[index]}")
                                    } else {
                                        rule.append(",${byMonthDayArray[index]}")
                                    }
                                }
                            }
                        }
                    }
                    4 -> {
                        rule.append("FREQ=YEARLY;INTERVAL=$customRepeatInterval")
                        customRepeatItems?.let {
                            for (i in it.indices) {
                                val index = it[i] - 1
                                if (index <= byMonthArray.size) {
                                    if (i == 0) {
                                        rule.append(";BYMONTH=${byMonthArray[index]}")
                                    } else {
                                        rule.append(",${byMonthArray[index]}")
                                    }
                                }
                            }
                        }
                    }
                }

自定义重复有重复评率,跟日期类型,自定义时间,比如每个月的1号重复

上面逻辑是判断自定义类型 customRepeatFreq,选择的是日,周,月,年,然后是自定义重复数 customRepeatInterval,最后是自定义日期 customRepeatItems,具体选择的某天或者某几天。

所以把系统规定好的标识都定义成集合,方便动态添加

 

    // BYDAY 周
    private val byDayArray = arrayOf("MO", "TU", "WE", "TH", "FR", "SA", "SU")

    // BYMONTHDAY 月-天
    private val byMonthDayArray = arrayOf(
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
    )

    // BYMONTH 年-月
    private val byMonthArray = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
View Code

重点就是在于规范的拼接,需要跟业务结合,然后同步到系统

 

然后是修改日历,这边修改也比较繁琐,并且重复日历修改没有系统那种关联性,系统可以识别一样的标题同步修改,程序只能自己循环修改,并且每次修改都要获取权限,兼容性结合业务不好操作,所以综合考虑,采取先删除在添加

 

而且居然需要修改,肯定对之前的提醒不满意,在次添加新的提醒也符合用户习惯。

日历操作对时间格式要求高,需要规定好时间格式,加强判断。

 

日程CalendarspancolorstyleAndroid开发

有关Android Calendar 系统日历提醒、日程同步系统的更多相关文章

  1. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  2. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  5. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  6. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  7. ruby-on-rails - 如何构建复杂的 Rails 系统 - 2

    关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭8年前。Improvethisquestion我们有以下(以及更多)系统,我们将数据从一个应用推送/拉取到另一个:托管CRM(InsideSales.com)Asterisk电话系统(内部)横幅广告系统(openx,我们托管)潜在客户生成系统(自行开发)电子商务商店(spree,我们托管)工作板(本土)一些工作网站抓取+入站工作提要电子邮件传送系统(如Mailchimp,自主开发)事件管理系统(如eventbrite,自主开发)仪表板系统(大量图表和

  8. ruby-on-rails - Rails 3,在RAILS_ROOT上方显示来自本地文件系统的jpg图片 - 2

    我正在尝试找出一种方法来显示来自不在RAILS_ROOT下(在RedHat或Ubuntu环境中)的已安装文件系统的图像。我不想使用符号链接(symboliclink),因为这个应用程序实际上是通过Tomcat部署的,而当我关闭Tomcat时,Tomcat会尝试跟随符号链接(symboliclink)并删除挂载中的所有图像。由于这些文件的数量和大小,将图像放在public/images下也不是一种选择。我查看了send_file,但它只会显示一张图片。我需要在一个格式良好的页面中显示6个请求的图像。由于膨胀,我宁愿不使用Base64编码,但我不知道如何将图像数据与呈现的页面一起传递下去。

  9. ruby - 我可以从 Ruby 中的系统调用中获得连续输出吗? - 2

    当您在Ruby脚本中使用系统调用时,您可以像这样获得该命令的输出:output=`ls`putsoutput这就是thisquestion是关于。但是有没有办法显示系统调用的连续输出?例如,如果您运行此安全复制命令,以通过SSH从服务器获取文件:scpuser@someserver:remoteFile/some/local/folder/...它显示随着下载进度的连续输出。但是这个:output=`scpuser@someserver:remoteFile/some/local/folder/`putsoutput...不捕获该输出。如何从我的Ruby脚本中显示正在进行的下载进度?

  10. u盘安装系统(win10为例) - 2

    下载微PE工具箱进入官网下载微PE工具箱-下载 安装好后,打开微PE工具箱客户端,选择安装PE到U盘 PE壁纸可选择自己喜欢的壁纸,勾选上包含DOS工具箱,个性化盘符图标 下载原版系统进入网站下载镜像NEXT,ITELLYOU如果没有账号,注册一下就好进入选择开始使用选择win10 这里我们选择消费者版,用迅雷把BT种子下载下来 下面的两个盘符,是PE工具箱安装进U盘后,分成的盘符,注意EFI的盘符,这里面不能删东西,也不能添东西,另一个盘符可以当做正常的U盘空间使用,我们现在需要把下载下来的景象文件复制到正常的U盘空间中去 这个时候我们的系统U盘就只做好了 安装系统我们将U盘插入电脑,开机,

随机推荐