2020/9/30

CMake cross-build -- served

用 CMake 來做 cross-build
-- 其實是因為大多使用 CMake 的 project 都是 host - target 同一系統,所以 detect, config 都是自動。但是現在要 build for target,就要讓 CMake 可以做 cross build.

看起來,是要用 -DCMAKE_TOOLCHAIN_FILE=maytoolchain.cmake
然後寫 mytoolchain.cmake
裡面指定toolchain path, name 和一堆 flag, library 等...

然後goole CMAKE_TOOLCHAIN_FILE 可以找一堆 sample,,



cross build served..

準備好 toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(TOOLCHAIN_PATH /usr/local/linaro-aarch64-2018.08-gcc8.2)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-linux-gnu-g++)

run cmake....
served.build$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain.cmake ../served
Error:
CMake Error at /usr/share/cmake-3.10/Modules/FindBoost.cmake:1947 (message):
  Unable to find the requested Boost libraries.

  Boost version: 1.65.1

  Boost include path: /usr/include

  Could not find the following Boost libraries:

          boost_system

  No Boost libraries were found.  You may need to set BOOST_LIBRARYDIR to the
  directory containing Boost libraries or BOOST_ROOT to the location of
  Boost.

in case cross-build boost: .. 結果沒用到,target system 的 boost 1.66.0 也可以用。所以不用 port boost

依照Error message,把 target system 的 boost package folder copy 到/tmp
要安排一下,把lib 和 header(include) 依照下面方式放好..
/tmp/boost/include/boost
/tmp/boost/lib
並且把 修改 toolchain.cmake,加入
set(BOOST_ROOT /tmp/boost)
一樣..
served.build$ cmake -DCMAKE_TOOLCHAIN_FILE=~/toolchain.cmake ../served
-- The C compiler identification is GNU 8.2.1
-- The CXX compiler identification is GNU 8.2.1
-- Check for working C compiler: /usr/local/linaro-aarch64-2018.08-gcc8.2/bin/aarch64-linux-gnu-gcc
-- Check for working C compiler: /usr/local/linaro-aarch64-2018.08-gcc8.2/bin/aarch64-linux-gnu-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/local/linaro-aarch64-2018.08-gcc8.2/bin/aarch64-linux-gnu-g++
-- Check for working CXX compiler: /usr/local/linaro-aarch64-2018.08-gcc8.2/bin/aarch64-linux-gnu-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning at /usr/share/cmake-3.10/Modules/FindBoost.cmake:801 (message):
  New Boost version may have incorrect or missing dependencies and imported
  targets
Call Stack (most recent call first):
  /usr/share/cmake-3.10/Modules/FindBoost.cmake:907 (_Boost_COMPONENT_DEPENDENCIES)
  /usr/share/cmake-3.10/Modules/FindBoost.cmake:1558 (_Boost_MISSING_DEPENDENCIES)
  CMakeLists.txt:71 (FIND_PACKAGE)


-- Boost version: 1.66.0
-- Found the following Boost libraries:
--   system
-- Could NOT find RAGEL (missing: RAGEL_EXECUTABLE) 
-- Could NOT find RAGEL (missing: RAGEL_EXECUTABLE) 
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE  
-- Performing Test COMPILER_SUPPORTS_CXX11
-- Performing Test COMPILER_SUPPORTS_CXX11 - Success
-- Performing Test COMPILER_SUPPORTS_CXX0X
-- Performing Test COMPILER_SUPPORTS_CXX0X - Success
-- Configuring done
-- Generating done
-- Build files have been written to: /home/charles-chang/githubgitlab/served.build
成功了...

到 served/bin/ 下 check file ,都是 ARM aarch64 ELF 沒錯。

放到 target board 上 run ..
  • 把 served/lib/ 下的so copy 到 /lib/ 下
  • 有用到 boost_system.so,所以要記得把 target build system 的 so 也 copy 到 /lib/ 下

RESTful server (library) in C++

直接link 到 main program 好像比較方便,不用再透過 interface call programm..
所以找 C++ library


有附example 和用 curl 的測試方法。
依照說明clone 下來,用 cmake 產生 Makefile 後 make 舊可以。
在 clone 的folder 下的bin 會有所有 example 的 執行檔,可以一一執行。
同時會印出 用 curl 的測試 command

example 的 main( ) 內容也很簡單。

build 好的 library 只有一個。
include 檔整個和 source code 合在一起。

剩下的問題就是 cmake 系統 porting 到 aarch64 的問題。




這個應該是最有名的,但是 example 看起來好像比剛剛的 served 複雜一些。


其他還有 microsoft 的 c++ rest api,這個 example 看起來更複雜:


上面的 library 比較簡單,所以沒有提供 authentication (login) 功能。
要有 login 的話,要用...

這個就有 authentication,同時支援 http 和 https,但是 library 比較大,使用起來也比較複雜。



這個也有支援 basic 跟 token authentication.
也有 https
也是依照callback, url hanelder 寫程式。


其他

cuda in docker

因為這一篇 說,照一般安裝也可以用。
當然,他提供了三種方法。
所以想試試看直接安裝的方法。
--- 上次在docker 中使用 nvidia ffmpeg,是用 nvidia 的docker。

follow 這一篇,無腦的裝了。

test 有 Error, 要 follow 這一篇 加上 nvidia 的 source list
才能裝
$ sudo apt install nvidia-container-toolkit
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libnvidia-container-tools libnvidia-container1
The following NEW packages will be installed:
  libnvidia-container-tools libnvidia-container1 nvidia-container-toolkit
0 upgraded, 3 newly installed, 0 to remove and 51 not upgraded.
Need to get 850 kB of archives.
After this operation, 2,623 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
重新啟動 docker
systemctl restart docker
然後在 container 舊可以正確run nvidia-smi
因為 host 是 10.2:
~/gpuindocker$ cat Dockerfile 
FROM nvidia/cuda:10.2-base
CMD nvidia-smi

build image and run
~/gpuindocker$ docker build . -t nvidia-test

~/gpuindocker$ docker run --gpus all nvidia-test
Thu Oct 29 08:07:02 2020       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.100      Driver Version: 440.100      CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  TITAN RTX           Off  | 00000000:01:00.0 Off |                  N/A |
| 30%   46C    P0    31W / 280W |      0MiB / 24218MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
這樣在container 中也能正確 access gpu

原來是docker 19.03 版之後,可以不用 nvidia-docker2 來 access gpu
可以直接支援 gpu


2020/9/28

memo .. about nmea parsing..

stringstream 的特性..
先用 getline( whole, sub, ',') 取出以 ',' 區分的字串。
然後用 sub >> float type 直接舊可以轉成 floating type 了。

讀取的部份,還是只能一次一次的,把 char buffer 讀進來,再轉 stringstream。

不行,stringstream getline 雖然會記住processing 的位置,但是 stringstream 本身不會消失,所以一直 append 的話,會越來越大,必須要刪除掉處理過的部份。


ref:

改用 string,find $ 之後找 '\n' 完成一個substring,assign 給stringstream。
然後再reassgin 剩下的substring 給自己。

read : char[]
find : string
parsing : stringstream

bookmarks: veracrypt , open source volume encrypting tool for Linux, android and Windows

2020/9/25

vim plug in

vi 有自己的 script language
vi plugin 就是用這些script language 寫的,用來完成特殊功能。

vim 有規定 plugin 的安裝方式:VIM REFERENCE MANUAL by Bram Moolenaar

這一篇有說明 vim 對 plugin 的運作原理。
vi 啟動,執行.vimrc
然後 search .vim/ 下所有的目錄,找 pack/*/start.
找到後執行他。

為了方便管理 plugin,也有人寫了 plugin 來方便 plugin 的安裝。

Vundle

所以要安裝其他plugin之前,要先安裝這個。
git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim
先把 Vundle clone 到 .vim 的folder..
然後修改 .vimrc,把 這個folder 加到 vim 的環境變數裡
set rtp+=~/.vim/bundle/Vundle.vim
接著 call Vundle 的 begin 和 end function..
在這兩個function 中間的 plugin,vundle 就會幫你安裝和管理。
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'
Plugin 'tpope/vim-fugitive'
call vundle#end()            " required
filetype plugin indent on    " required

以dart 來舉例,照著這一篇 做..
修改 vimrc,加入 Plugin 'dart-lang/dart-vim-plugin':
set nocompatible  
filetype off
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'VundleVim/Vundle.vim'
Plugin 'dart-lang/dart-vim-plugin'
call vundle#end()
filetype plugin indent on 
開啟vim,輸入 :PluginInstall,之後會開啟 Plugin List,然後出現Done!,舊完成了。
ls ~/.vim/bundle 可以看到..
dart-vim-plugin  Vundle.vim
已經幫你下載 dart-vim-plugin 了。

ref:

上面是第一次安裝,使用 plugin 的時候,可能參考到 Vundle ..所以寫的。
之後,又遇到,chatgpt 建議,用 vim-plug
其實vim 有 啟動後,自己到特定目錄load vim script file 的動作,所以手動把 plugin (也就是 vim script file) copy 到個特定目錄下也可以。
但是為了管理方便,有人就用 vim script 寫了 plugin 管理程式。
基本上自己也是一個 vim script。
vim script 提供一堆 function /command,要執行的話,就是再 vim 下 的命令 :function ,其中 function 就是 script 提供的 function 名稱。

所以,要安裝 vim-plug,就是把github 網站的 plug.vim 單一檔案,下載到 .vim/autoload/ 下。
這樣啟動的時候,vi 就會去 load 他了。
另外,要在.vimrc 寫:
call plug#begin('~/.vim/plugged')

"Put your plugin here

call plug#end()
這樣,vi 啟動後, run .vimrc,就會執行 call plug, 分別begin 跟 end.
讓 vim-plug 去處理 安裝的 plugin。

所以,把要安裝的 plung 寫在 .vimrc 的 begin(), end() 之間就可以。

2020/9/22

request_firmware : read file from kernel

kernel driver 可以從 filesystem 中讀檔。
用 request_firmware( ) 這個function。

file 的 path 呢?
在 driver/base/firmware_class.c:
/* direct firmware loading support */
static char fw_path_para[256];
static const char * const fw_path[] = {
    fw_path_para,
    "/lib/firmware/updates/" UTS_RELEASE,
    "/lib/firmware/updates",
    "/lib/firmware/" UTS_RELEASE,
    "/lib/firmware"
};

/*
 * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH'
 * from kernel command line because firmware_class is generally built in
 * kernel instead of module.
 */
module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
其中 fw_path_para 是 啟動 kernel 時指定的

2020/9/21

Bookmark: denoise , deblure with NN.

這個很有趣,Super Resolution W.N.N

作者有 opensouerce 喔
* SRWNN Mobile : 是用 flutter 包 tenerlite。

upscale 的 GAN 部份是參考waiful2x 也有為準備training data 寫的 tool:PuixivUtil2

NN 的部份是基於ESRGAN,這個有詳細的實做和 training 方法
然後這個E2_ESRGAN 就是 Tensorflow 的實做
這個是 SuperResolutionWaifuNN的實做。

2020/9/17

自動駕駛的等級..

0-5 個 level 各是什麼很難記。
看到這個,原來是這個意思。

開車需要用到
  • 眼睛
所以,,,
  • 0 : 全用到
  • 1 : 不用腳
  • 2 : 不用手
  • 3 : 不用眼睛
  • 4 : 不用腦
  • 5 : 全部都不用之外,駕駛也不用了

2020/9/15

Prepare minimum kernel sources for building external module

build external module

由於 build module 需要用到 kernel 的 Makefile (和 Kbuild)。
所以必須要有 kernel 的 Makefile。
而用 make headers_install 做出來的 kernel headers 是不含 Makefile 。
所以必須要完整的 kernel source。

另外,必須要知道 kernel 的 配置,所以要包含 target 的 .config。
另外,還包含 arch link 和 generated header。
簡單的說,就是 source 還必須要 build 過。

Makefile 中,可以用 -C 指定 source,O 指定 output (當初build kernel 的 output)。
不然,就要放在一起。用 -C 指定。

先用標準的方法來試試看...
這個 就是hello.ko。
先 clone and build kernel:
  • git clone --depth=1 https://github.com/raspberrypi/linux -b rpi-5.4.y pilinux
  • git clone https://github.com/raspberrypi/tools.git pitool
  • export PATH="/home/charles-chang/pitools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH"
  • cd pilinux
  • make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
  • make -j 40 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs
這樣就完成了需要的 kernel source

之後,一樣把 path 設好,到 helloko 下 make 舊可以 build 出給 pi 用的 hello.ko 了。


這是用完整的kernel source,但是make external module 只需要 Makefile, Kbuild, header,config。
所以可以把所有的 .c, .o, configs 目錄,都刪掉,


build kernel module 就是利用 kernel source 的 Makefile。
kernel 的 Makefile 有 support 一堆 option..
ref:kernel makehelp

helloko 的Makefile 用到..
  • V=1 : 印出所有的 make 時的動作
  • CROSS_COMPILE=arm-linux-gnueabihf-
  • ARCH=arm
  • -C $(KERNEL_DIR) : kernel source 位置
  • M=$(PWD) : module 位置


遇到很有趣的事...
在把 cypress wifi 移出 kernel source ,改 external module 來 build 的時候...
Kbuild 中,有..
DHDCFLAGS += -DCONFIG_BCMDHD_FW_PATH=$(BCMDHD_FW_PATH)
BCMDHD_FW_PATH 是環境變數,在 make 前先 export 好..
因為是字串,所以用 esc '\' 來加 '"' 符號...
export BCMDHD_FW_PATH="\"/lib/firmware/cypress/cyw88373/fw_cyw88373.bin\""
結果 fail..
source code 中說 BCMDHD_FW_PATH 是 null..

對照成功的 log (build within kernel source),發現這個字串包含 esc code..
所以再esc 一次..
export BCMDHD_FW_PATH="\\\"/lib/firmware/cypress/cyw88373/fw_cyw88373.bin\\\""
就 OK 了。

從一般的SDK 的 kernel source 準備一份可以 build external module 的 kernel header 的方法:
  • make kernel image OK (好像是 make config 跟 make prepare 就可以?)
  • copy kernel source (include hidden file)to /tmp/linuxheader
  • copy kernel out (include hidden files) to /tmp/linuxheader
  • remove all *.o,*.c,*.cmd
  • copy kernel source/Makefile to /tmp/linuxheader because it was overwritten by the out source
  • modify driver Makefile, obj-$(DRIVER_TYPE) += 要改成 obj-m :=

2020/9/14

IIS2DLPC accelerometer, polling mode. datasheet and driver

IIS2DPLC Datasheet

ODR : output data rate
依照 High-Performance/ Low-Powerer Mode 而有不同的值:
  • 0: power down
  • 1: 12.5/1.6Hz
  • 2: 12.5Hz
  • 3: 25Hz
  • 4: 50Hz
  • 5: 100Hz
  • 6: 200Hz
  • 7: 400/200Hz
  • 8: 800/200Hz
  • 9: 1600/200Hz
但是 driver 的寫法卻是 high-performance/lowe-power mode 都是一樣

Driver Code 看起來,用hrtimert 做 polling,跟 ODR 沒有同步。
只是根據 ODR 設定 timer polling time

因為 implement 成 input device,所以會依照 timer 主動 report input event

2020/9/11

aplay with repeat option, and cross build

aplay 竟然沒有自動repeat 的選項。
所以clone 一個版本下來改。

aplay 屬於 alsa-utils,但是github的版本,需要自己產生 configure 檔。
要做 cross-compile 的話很麻煩,
所以用 LFS 的 1.0.26 板來改。

只是在找到 最後read file to pcm 的地方,重複做而已。
然後記住 開丟到 pcm 的file position

另外,因為r, l 都被用掉了。只有選 'b' 了.

放在 alsa-utils-1.0.26-with-repeat

aplay -b myfile.wav
要停止舊只能 Ctrl-C 了...


cross build

他要兩個 library:
  • ncurses
  • alsa-lib
export 好 path to toolchain.
然後宣告 CC
export CC=aarch64-linux-gnu-gcc
再來 ru ./configure..
LDFLAGS+=”-L/tmp/ncurse/lib -lpthread -lm -lc”; CFLAGS+=”-I/tmp/ncurses/include -I/tmp/ncurses/include/ncurses”; ./configure --build=i686-linux --host=arm-linux --prefix=/tmp/alsa/utils --with-alsa-prefix=/tmp/alsa-lib/lib --with-alsa-inc-prefix=/tmp/alsa-lib/include --disable-alsamixer
其中 /tmp/XXX 是需要的 library,把target 的 library copy 到...

然後舊可以 make 了...

Get current file position , lseek()

Linux 的低階 IO,提供 open, close, read, write 和 lseek。
lseek 就是用來移動 read. write position
參考 lseek definition in gnu
off_t lseek (int filedes, off_t offset, int whence)
其中的argument 都是設定用的,用來修改 read/write posisiton

那要如何知道目前的position 呢?
因為每次呼叫 lseek(),他都會傳回修改後的position (鄉對於開始)。
所以就做一次 dummy set 舊可以了..
stackoverflow: ftell on a file descriptor
position = lseek(fd, 0, SEEK_CUR);

bookmark : comparison chart of libc , 其實是在看 musl-libc

其實是因為看到musl-libc,想說怎麼沒看過。
甚至有些distribution 用這個 C library
光看官網介紹也無法了解為什麼要再做一個新的。

這一篇列出各 c library 的性能比較。

... 雖然是這樣,好像還是看不出再重新寫一個的目的是...?

alphine採用 musl-libc 來看,可能是因為 size 最小,
然後這一篇 說,musl 的 malloc 好像有點問題...

這一篇 有很好的範例說明如何使用 musl-libc
的確,為了方便,需要 build 一個 default 使用 musl-libc 的 toolchain(gcc)
clone 下來,./configure , make 之後 舊 OK (make install ?)
同一份source code ,用 gcc build,就 link glibc
用musl-gcc build,舊 link musl-libc
然後舊可以比較 size. speed... etc

2020/9/10

bookmark : drunk, sober , face.

Drunk-Detect

如果有清醒(Sober) 時的資料,以此判斷他是否清醒(的臉),好像比較可行。

dif dataset of intoxicated faces for drunk person identification

這個是用來training 的database,還有用比較的方式(LSTM) 來偵測 drunk person

2020/9/8

cross build ncurses library

ncueses 的 6.1, 6.2 在 cross build 的時候都會有這個問題...
make install 會 Error,說 strip error

原因是install 這個tool用到host 的strip,這個解決方法比較....

查到 install --help,有..
--strip-program=PROGRAM
的option,所以修改每一個有 target: install 的 Makefile...

結果這一篇也有說。
--- a/configure	2018-01-19 19:27:18.000000000 -0500
+++ b/configure	2018-03-14 19:18:39.136573491 -0400
@@ -13716,7 +13716,7 @@
 
 if test "$with_stripping" = yes
 then
-	INSTALL_OPT_S="-s"
+	INSTALL_OPT_S="-s --strip-program=${STRIP}"
 else
 	INSTALL_OPT_S=
 fi
他修改configure,吃 STRIP 這個環境變數。
同時,所有Makefile 都會改到。

這樣改完,重新 configure 後,6.1 的 make install 沒問題了。
6.2 還有問題,是 color 什嗎的問題,還沒解決..

2020/9/7

flutter plugin: get external sd card path : path_provider_ex

plugin: path_provider_ex

其實只是用來找到 SD card 在系統的 full path

plugin 的 example 在有sdcard 的手機和沒有sd card 的手機 (pixel2) 分別顯示不同的內容:


現在把example 轉成 android project, 放在 github: pathproviderex

啟動log 程式,靜止,log 1min。取得校正點。
地心引力 g 的方向(三軸分量)

之後持續log ...

對照

1sec內所有的 data 做 average
果然變得很smooth,很漂亮。

2020/9/4

flutter : 修改 package 來用

寫flutter 就是要用一堆別人寫好的 package.
但是如果 package 不是100% 符合需求呢?
可不可以clone 下來改?

ref:How to modify plugins Dart code flutter

原來我們pub get 完的 package(plugin) 都會在 ~/.pub-cache/hosted/pub.dartlang.org/ 下面。
一個package/plugin, 一個 version 一個folder

找到要改的plugin folder,copy 到自己的 project home 下 (跟 pubspec.yaml 同一層),rename folder name.
然後改自己的 pubspec.yaml,原來 depencies 部份:
sensors:
加上 path:
sensors:
  path: ../mynewfoldername/
這樣就可以了。

做了一個把 sensor 拿進 project 的 code: accelerlog

原本是想修改 sensor onListen 的 frequency 的,可惜沒有效。
最後還是用 check second value changed 的方式。

2020/9/3

flutter_logger : android version

就是這篇文章:
A Flutter file logger (for iOS and Android simulators)


裡面的 code,我把他放到 Android Run
然後把不能動的地方修改一下..
  • AndrodManifest.xml 加上 user-permission for external storage read/write
  • main( ) 要加上async, 和 ensureInitialized( ) 不然會 compile Error 跟 run 不起來
  • 要用 getExternalStorageDirectory(), 不然雖然執行沒問題,但是沒辦法用 FileManager 找到位置
Project 放到github 了。

附帶寄一下..

用 getApplicationDocumentsDirectory() 的話,位置是:/data/user/0/com.example.flutter_logger/app_flutter/back_to_now.txt
這個用 adb 去要 root 才能打開。

用 getExternalStorageDirectory(),位置是 /storage/emulated/0/Android/data/com.example.flutter_logger/files/back_to_now.txt。
不用 root 舊可以看了。

還有,用上面兩個位置。都不需要用 permission_handler 來 request access permission,只要修改 AndroidManifext.xml 舊可以

flutter: synchronized

在看flutter logger 的時候看到,writelog 時,要用synchronized 這個 package

這跟 async 有什嗎關係?

synchronized 的官方說明 中的 example 有很好的說明。
他的 write(1234) 剛好符合這個 logger 的狀況。

因為writelog( ) 是一個 critial section (mutex ?)
一個 writelog 沒寫完時,下一個 writelog 的function 要 wait.
因為 writelog 又是 async function,所以需要 synchronized 這格 package,把 write( ) 的 code section guard 起來。

2020/9/2

flutter : permissions

其實是要做file read/write 的,結果卡在 external storage permission...

太久沒寫,user-permission 要寫在那一層都忘了...
寫在最外層,跟 application 同一層。(不是application 之下)
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.example">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<application android:name="io.flutter.app.FlutterApplication" android:label="example" android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
然後是pubspec.yaml
dependencies:
flutter:
sdk: flutter
simple_permissions: ^0.1.9
path: '>=1.5.1 <3.0.0'
path_provider: '>=0.4.0 <3.0.0'
這個simple_permissions 最後沒有成功,因為用了舊版sdk 的樣子。
所以還要去修改 android 的 gradle.
-- 這樣舊失去了 flutter 的意義。

後來用了 permission_handler

參考了Request Permissions in Flutter as a Service 的 example code,放在project file:github: permissionhand

根據plugin 的官方 example 做的 project 在: phexample