路先行 CarPlay 盒子升级

2025年10月15日 没有评论

在大约8+个月前,我买了个CarPlay 的有线到无线的转换器,寻思多年未使用的功能终于可以派上用处了。

这东西不太放心,怕影响了现有的车机系统,并没有开放使用。操作起来主观感觉还将就,可以感知到延迟,但还在可以接受的范围里。

主要是手机支架是面向我的,看导航信息很是方便,用了这个后看导航要偏头看屏幕,不是很习惯,而且地图的导航界面和手机的布局不太一样,一时之间并不适应。

所以这个设备就放这里了,希望以后有机会可以体验原生的车机系统。

对于盒子的升级,问了下客服,整理步骤如下:

  1. 先将盒子插入到电脑的 U盘口,或者手机的充电器上。
  2. 手机搜索 wifi 热点,会发现一个类似 smartBox-43AB 格式的热点。
  3. 手机接入这个热点后,会发现“无互联网连接”的提示,这是符合预期的,毕竟它不是一个可以上网的路由器。
  4. 手机浏览器访问 192.168.1.101,就会在页面里发现盒子的各种参数信息。滑动到页面底部,会发现一个区域是关于升级的。如果有新版本,点击更新就好了。
  5. 升级完成后,插入到车上,重新连接。如果需要密码,那就是 88888888

对了,这个盒子牌子叫做“路先行”,某宝上另外一个常见的牌子是君用。对于君用的那个盒子,一个是价格原因,另外有人反馈延迟较高,就没有买。

我买的这个,插入到扶手箱里的 USB 接口上,运行一段时间后,可以感觉到发热,但也不至于不能碰。

其实到现在,我发现了一个使用这个盒子的场景了。我有一个专门的手机壳,磁吸的方式放到中控台上。如果是别人要用导航的话,就可以连接到这个盒子上,毕竟别人的手机壳可不是磁吸的,尽管我也留有备份的磁吸片。

对了,一般而言,CarPlay 盒子都区分苹果和安卓的,大家在购买时需要确认下是否和自己的手机适配。不过,衣服自己洗这里还是希望大家的小车车都自带这样的功能,毕竟时代在进步是不是。

分类: 日常 标签:

磁盘取消 BitLocker 加密

2025年9月11日 没有评论

BitLocker 这个东西吧,好也不好,一言难尽。反正衣服自己洗是倾向于给关闭了的。

打开控制面板->系统和安全->BitLocker 驱动器加密,这个时候弹出的界面上就会显示加密状态。对于多个分区,可以分别关闭。

但是发现磁盘上还是有个感叹号图标,奇怪的很。

继续检索,又有一个思路,Windows 设置中,搜索“加密”,打开“设备加密设置”,把右边的开关给关掉。

悲催的是,系统弹窗说无法关闭,即使重启系统后,也不行。

继续检索,又看到方法,命令行输入:

manage-bde -status

输出结果里可以看到,显示加密暂停。

继续执行指令:

manage-bde -pause C:

注意,修改这里的盘符为实际的值哈。

输入命令后,不要着急,耐心等待几分钟,之后使用 manage-bde -status 来查询状态。如果显示结束了,那么再回到刚才的设置窗口里,把开关给关闭。

之后再重启一次电脑就可以了。

分类: 日常 标签:

新电脑直接激活 Administrator 账号

2025年8月4日 没有评论

全新的办公电脑从去年发给我之后,一直没有打开。今天,终于打开并开始安装所需要的环境了。

在以前,我一直是先创建一个用户,进入系统后,然后激活默认的 Administrator,之后再删除新创建的用户。这么做有一个缺点就是即使在用户管理界面里删除了,但是用户目录还在。虽然手动删除掉似乎没有什么问题,但是对于强迫症,总觉得应该有更干净的做法。

这次借这个机会,就搜索了一番。你还别说,真有方法。来,手把手的步骤来了。

开机后,系统会进入欢迎和配置界面,让你选择地区、键盘布局、联网激活等等,行话叫做 OOBE。其实小软有一点不够地道,就是为啥非要默认让你联网。

直接在第一步,使用快捷键:Shift + F10,如果你的电脑默认 F10 是功能键的话,那么你还需要额外加一个 Fn 键。这时会打开一个命令行界面,在里面输入下面的指令:

net user Administrator /active:yes

然后再输入下面的指令:

oobe\msoobe && shutdown -r

请注意,网上有的文章里也提及了 oobe\bypassnro,和这里的不太一样,虽然我不清楚两者之间的区别,照葫芦画瓢就好了。

输入命令后,系统会重启,也不会要求联网了。不过印象中还是会有部分信息设置,例如跨境数据传输之类的,其实也没得选,下一步就好了。

不过衣服自己洗这里,还看到一个系统弹窗,提示账户不存在,没有管,也正常进入系统桌面了。

事情到这里其实我以为就结束了,事实上并不是这样。

衣服自己洗是如何发现的呢?就是我发现运行任何程序,都没有弹出 UAC 界面,看起来就好像任何程序都直接获取到管理员权限了。这可不是太好的行为。检查了 UAC 的弹窗设置,看着也正常。网上的教程都是教你如何去除 UAC,衣服自己洗这里需求却是反过来了。

在常见的系统设置的那几个地方,和另外一个电脑比较,都是一样的配置,并没有发现什么特别的地方。这不就奇怪了么~

一番搜索,还是要上组策略,计算机配置->Windows配置->安全设置->本地策略->安全选项,里面列表的后半部分,“用户账户控制:用于内置管理员账户的管理员批准模式”,双击,选择启用。

最后,重启电脑。

嗯,再运行一些程序,熟悉的 UAC 弹窗又回来了。

分类: 日常 标签:

老款 Mac 强制升级到新版本(续)

2025年7月7日 没有评论

在前面的文章中,我有实际操作从原本的 High Sierra 升级到 Catalina, 升级后系统可以感知到明显的卡顿,毕竟 i5双核的CPU+512机械硬盘这样的硬件配置在这里。

后来又在网上发现了 Github 开源的 OpenCore Legacy Patcher 这个工具,可以把玩一番,但是衣服自己洗这里没有去升级,主要是当前的性能跟不上了,另外一个原因是会变黑苹果。

变黑苹果的原因是这个工具会重新生成序列号。有兴趣的朋友可以看看 B 站视频,我这里提供两个地址:

https://support.apple.com/zh-cn/109033
https://www.chiphell.com/forum.php?mod=viewthread&tid=2574432

分类: 一句话 标签:

老款 Mac 升级记录

2025年6月7日 没有评论

我那个2011年的老 Macbook Pro停留在 High Sierra(10.13.6)再也无法获得更新,这是我的第一台水果电脑,当年学习iOS开发可是出了不少力,所以对这个电脑很有感情的。

在吃灰了好多年后,昨天有突发奇想就给开机了,这一开机后,就想着怎么给升级系统,如果升级后XCode 可以支持我的 iPhone 8 就更好了。

经过一番搜索,发现了一个叫做 macOS Catalina Patcher 的开源工具可以帮忙实现升级到 Catalina(10.15.7),这就不错了,升了2个版本的系统。

下载完成并解压缩后,打开这个工具,你可以看到一个选项窗口,询问你新的Catalina系统,如果你已经有系统副本,可以选择 Browse for a Copy。如果没有,就点击 Download 来下载一个,大概有8G左右。对于衣服自己洗来说,当然是直接下载比较直接了。下载略微需要点时间,不过感觉还好。

接下来,选择安装方法,我直接安装到系统盘,已经是老mac了,大概率也不会有问题。当然你还可以选择第二个选项,准备一个16GB 的 U 盘来安装。第三个是光盘安装,现在光盘和光驱已经很少见了。

后面就是无脑下一步了,下载完成后,点击安装。安装过程中,你会看到一些提示和选项。按照提示一步步操作就行。这里就不说细节了,因为已经忘记了。

接下来就是设置新系统了,电脑会自动重启几次。最后算是升级成功了吧。

说下我的感觉:

  1. 升级后电脑的发热量没有明显的变化,但是系统响应变慢了点。
  2. WiFi 无线会抽风断网,不知道是否和这个有关系。
  3. 原本 /Code 文件夹,给挪到桌面了,说是不兼容,估计因为是放根目录的原因。
  4. 升级后终于可以安装 .net8 和 Jetbrains 全家桶了,就是慢。

感谢观看。

分类: 日常 标签:

安装指定版本的 XCode

2025年5月17日 没有评论

在前面的文章里,我有提及强制升级 mac 的系统,但是由于 XCode 和系统的深度绑定,我们也需要升级 XCode 到对应的版本里。

众所周知,XCode 是可以从 Store 里下载的,但是 Store 里提供的版本太新了,并不能支持当下的操作系统,所以就需要手动找地址去下载了。

考虑到以前出现过 XCode 被人植入过木马病毒,这东西即使体积巨大,也只能是通过官网下载最为合适了。

但是官网并没有直接显眼的方式提供,也可能是我使用的方式不对。后来找到了一种方法,浏览器打开 https://developer.apple.com/download/all/?q=xcod ,然后在该页面里搜索你需要的 XCode 版本号,点击下载即可。

为了稳妥起见,建议同时也下载 xcode-select 工具,在页面里一般是和 XCode 成对出现的。

需要提醒两点:一是可能需要登录,所以没有买开发者账号的就比较绕了点。第二个就是上面的查询关键词是 xcod,而不是 xcode。我自己试了下,输入完整的反而搜索不到想要的结果。所以直接复制这里的地址就最简单了。

下载完成后,直接双击解压缩,得到 XCode.app,拖动到应用程序目录里,就可以了。

分类: 一句话 标签: ,

react-i18next 进阶用法

2025年4月14日 没有评论

在过去的 React 和 React Native 项目里,对于多语言处理,使用的是 react-i18next 来处理的。今天给大家分享下这个库的一些进阶用法和注意事项。

其实一开始的时候,我自己是有点困惑的,react-i18next 和 i18next 是什么关系?为什么项目要安装这2个库,而不是一个?而且为什么在代码里如何从 i18next 里导入某些变量会出现非预期的结果。所以这里也给小伙伴解释下,react-18next 是对 i18next 的 react 版本封装。换句话来说,一般情况下,大家直接使用 react-i18next 库就可以了。

安装很简单:

npm install react-i18next i18next --save

进阶一、兼容性问题

如果你的代码在运行时出现了下面的错误:
i18next::pluralResolver:
Your environment seems not to be Intl API compatible,
use an Intl.PluralRules polyfill.
Will fallback to the compatibilityJSON v3 format handling.
那么按照官方的说明(https://www.i18next.com/how-to/faq#why-are-my-plural-keys-not-working),表示当前环境不兼容 Intl API。

解决方法:

npm install intl-pluralrules --save

然后在多语言的初始化文件里导入:

import 'intl-pluralrules'

这取决于你的 项目结构,可能就在 index.js 里,我们的项目就是在 i18n.ts 文件里。

这个问题,在我们的 React Native 项目里有碰到过。

进阶二,传字段

例如有下面的 json 文件:

{
    "error": {
        "required": "{{field}}必填",
        "invalid": "无效的{{field}}"
    }
}

那么使用时,就是这样使用的:t(‘error.required’, { field: t(‘Email’) })

进阶三,自定义多语言层级分隔符

默认情况下,代码里使用 . 来表示层级,例如 error.required,当然也可以使用下面的配置来更改为使用 “/”:

i18n.use(initReactI18next).init({
  // ...
  keySeparator: '/'
})

那么前面的调用就成了 t(‘error/required’, { field: t(‘Email’) }),我是觉得这个一般情况下是不必无苦硬吃的,可能在特殊场合需要使用。大家看看就可以。

进阶四,自动单复数

i18next 对于自动单复数有预定的约定,只需要在 key 后面添加 “_other” 即可,当然前提是对应的语言描述有复数的用法哈。

{
"hours": "{{ count }} hour",
 "hours_other": "{{ count }} hours",
}

t('hours', { count: 5 }) 
// 5 hours

对于 0 来说,实际来看,会使用复数的翻译,例如 0 hours。如果需要为 0 来单独定义的话,可以添加 “_zero”,例如:

"hours_zero": "Now"

需要提醒的是,如果这里内置的 _zero、_other 和你的多语言冲突了,还是有这个可能的,那么可以修改使用自定义分隔符,例如下面这样:

// i18n.ts
i18n.use(initReactI18next).init({
  ...  // 其它配置   
  pluralSeparator: '__', // 双下划线
})

具体使用:

{
    "days": "{{count}} day",
    "days__other": "{{count}} days",
}

需要注意的是,作为内置的约定,使用自动单复数时,参数固定使用 count,使用其它的就无法解析。

进阶五,Trans 组件

这里直接借用网上的一个示例,假如要显示下面的文字“你好 {userName},你有 {count} 条未读消息。点击查看。”,并且还需要支持点击操作。

在以前的写法中,我们需要将这句话单独的拆开,使用不同的组件包装起来,例如 count 希望是一个
红色的字体之类的,还要放到不同的多语言 key 里。太费劲了。

使用 Trans 组件,可以这么写,大家琢磨琢磨,是不是简单很多。

// en.json
{
    "userMessagesUnread": "Hello <1>{{userName}}</1>, you have {{count}} unread message. <5>Click to view</5>.",
    "userMessagesUnread_other": "Hello <1>{{userName}}</1>, you have {{count}} unread messages.  <5>Click to view</5>."
}

<Trans
    defaults={t('userMessagesUnread')}
    values={{ count, userName }}
    parent={Text}
    components={{
        1: <Text style={{ fontWeight: '500' }} />,
        5: <Text style={{ color: '#4682A9' }} onPress={() => {}} />
    }}
/>

可以看到,通过使用 components 属性,借助索引可以将指定位置的元素给替换包装起来。那么有人就问了,为什么这里是数字1和5,而不是其它的呢?

Trans 组件将上面的文本转换为下面的结构:

[
  'Hello ',
  { children: [{ user_name: 'Admin' }] },
  ', you have ',
  { count: 10 },
  ' unread messages. ',
  { children: ['Click to view'] },
  '.'
]

这么来看,是不是基于 0 的索引位置就出来啦。

更多内容,可以参考官方文档说明。

分类: 日常 标签: ,

Java 里的元组使用

2025年3月25日 没有评论

在最近的 Sping boot 项目里,想要一次性返回多个对象,按照过去 .Net的经验,很容易想到的就是使用元组 Tuple,但是第一下居然没有找到,不过有 HashMap,看起来似乎也是不错的选择,但是存在需要强制转换的情况,某些场景下可能会出错。

不是很死心,于是就搜索了一下,找到了 vavr 这个库,比较简单,大家可以凑活使用。

首先是在 pom.xml 里添加依赖:

<!-- https://mvnrepository.com/artifact/io.vavr/vavr -->
<dependency>
    <groupId>io.vavr</groupId>
    <artifactId>vavr</artifactId>
    <version>0.10.6</version>
</dependency>

使用 Tuple.of 来创建对象代码示例:

Tuple2<String, LocalDateTime> element = Tuple.of("Name", LocalDateTime.now());

创建两个元素的是 Tuple2,三个元素就是 Tuple3,这个命名方式有点特别,哈哈。

更新元组里的内容:element.update1(“名称”); 也就是 update[1-n]的方式。

读取元组元素的内容,element._1() 是获取第一个值,element._2()就是获取第二个值,以此类推。

这个库是 Apache 协议,可以商用,不错。

我又继续搜索了下,元组只是其支持的一个功能,函数式编程也支持,andThen、compose 看起来好高级哇。

异常处理的写法也不错,我简单地复制下代码:

Try result = Try.of(() -> 0)
                .map((a) -> 10 / a) //即使此处抛出异常,不会导致当前线程结束。这里无需使用 try{}catch()对代码进行捕获
                .andThen(() -> System.out.printf("--抛出异常此处不会执行--")) //执行一个动作,不修改结果
                .map(i -> {
                    System.out.println("当前值:" + i);
                    return i + 10;
                })
                .onFailure(e -> e.printStackTrace())//失败时会触发onFailure
                .recover(ArithmeticException.class, 1000) //如果遇到 Exception类型的异常,则返回1000
                .map((a) -> a + 1);

        System.out.println("是否抛出异常:" + result.isFailure());
        System.out.println("执行结果:" + result.getOrElse(100)); //如果有异常发生,则返回100

延迟计算,这个也高级,但是潜意识感觉,如果用的不好,很容易给自己挖坑。

Lazy<Double> lazy = Lazy.of(Math::random)
                .map(i -> {
                    System.out.println("-----正在进行计算,此处只会执行一次------");
                    return i * 100;
                });
        System.out.println(lazy.isEvaluated());
        System.out.println(lazy.get()); //触发计算
        System.out.println(lazy.isEvaluated());
        System.out.println(lazy.get());//不会重新计算,返回上次结果

还有另外一种线程操作的写法:

System.out.println("当前线程名称:" + Thread.currentThread().getName());
        Integer result = Future.of(() -> {
            System.out.println("future线程名称:" + Thread.currentThread().getName());
            Thread.sleep(2000);
            return 100;
        })
                .map(i -> i * 10)
                .await(3000, TimeUnit.MILLISECONDS) //等待线程执行3000毫秒
                .onFailure(e -> e.printStackTrace())
                .getValue() //返回Optional<Try<Integer>>类型结果
                .getOrElse(Try.of(() -> 100)) //如果Option 为 empty时,则返回Try(100)
                .get();
        System.out.println(result); // 1000

大家放心用吧,一用一个不吱声。

分类: Java 标签:

解决 React Native: unable to load script 的问题

2025年3月7日 没有评论

最近又创建了一个 React Native 的项目,以前踩过的坑又忘记了,所以需要记录下来,真是好记性不如乱笔头。

现象是这样的:在有了基础页面,并运行了 npm run start 本地编译后,无论是安卓模拟器还是真机,都报告错误:

Unable to load script. Make sure you're either running Metro (run 'npx react-native start)or that your bundle index.android.bundle is packaged correctly for release.

开始的时候,以为是因为 React Native 升级到 0.78 的版本导致的,因为从 0.76 开始提示会有新的开发者工具,从0.78 开始使用新开发者工具。在回退了版本以后发现还是有这个问题。

经过检索,一个快速的解决方法是在 Android 的 Manifest 文件中加入

android:usesCleartextTraffic="true"

错误原因分析,应该是应用默认不再支持明文传输了,但是本地调试的 metro 服务,脚本传输时使用的又是 http 的数据流,导致无法获取到脚本,从而产生了前面提及的错误。

当然,在生产环境下,还是建议不要使用明文传输了哈。

分类: Android, 一句话 标签:

NAS 上使用 Let’s encrypt 动态 DNS 续期 SSL 证书

2025年2月2日 没有评论

背景是我在 NAS 上部署了 immich 来存放照片,基于方便使用和安全考虑,使用 https 可能更合适。熟悉 immich 的小伙伴可能知道,这个应用需要使用反向代理来支持 SSL,虽然对于不能直接支持感觉不可思议, 但是这也不是太大的问题,开个 nginx 镜像也就实现了反向代理,关键是如何支持 SSL 证书。

现在家庭使用的基本上都是动态 IP,想要正确解析域名到实际 IP 就比较麻烦,好在 NAS 上支持动态 DNS 的解析,衣服自己洗使用的是 DNSPod,所以从里面导出一个 api key,放到 NAS 里就可以了,因为只需要很简单地无脑下一步,所以这里就不展开描述了。

但是,接下来我有碰到一个问题,由于对于家庭 IP 地址来说,80 和 443 端口一般是被运营商给禁止了的,所以 DNS 校验的方式就走不通,退而求其次看域名解析,而 certbot 又不支持 DNSPod 的,搜索了一番,网上有现成的,于是做了一个 docker 镜像,在 NAS 上部署。

第一步:克隆仓库

git@github.com:chenlongqiang/docker-certbot-dnspod.git

第二步:构建镜像

cd docker-certbot-dnspod
docker build -t certbot-dnspod .

请注意,上面命令最后还有一个英文的句号哟。

第三步:更新 dnspod.ini 配置,可以在 https://console.dnspod.cn/account/token/token 进行 DNSPod Token 秘钥创建。

第四部:部署镜像

# 申请新证书,执行后会进行命令行交互,填写完信息后将在 certs 目录下生成申请成功的证书
docker run -it --rm -v ./conf/dnspod.ini:/data/certbot/dnspod.ini -v ./certs:/etc/letsencrypt certbot-dnspod -d your_domain.com -d "*.your_domain.com"

# 续期 certs 目录下的证书
docker run -it --rm -v ./conf/dnspod.ini:/data/certbot/dnspod.ini -v ./certs:/etc/letsencrypt certbot-dnspod renew

请注意,如果不需要一次性,那么可以不添加 –rm 参数。同时请修改 dnspod.ini 的路径为你的实际路径。

经过验证,确实可以获取到 Let’s encrypt 生成的证书,然后更新到 nginx 的路径里。

不过,一个 https 的地址还带端口号,看着就怪怪的。

分类: 日常 标签: