Android取得裝置的語言
裝置語言取得如下
# get(0)為第一語言、get(1)為第二可能使用的語言
Resources.getSystem().getConfiguration().getLocales().get(0)
#輸出
zh-Hans-CN
zh-Hant-TW
還有一種方式,但比較不精準
#中國與台灣一律為zh
Locale.getDefault().getLanguage()
裝置語言取得如下
# get(0)為第一語言、get(1)為第二可能使用的語言
Resources.getSystem().getConfiguration().getLocales().get(0)
#輸出
zh-Hans-CN
zh-Hant-TW
還有一種方式,但比較不精準
#中國與台灣一律為zh
Locale.getDefault().getLanguage()
API 31(含)以後,寫法變了,
https://developer.android.com/reference/kotlin/android/net/wifi/WifiManager#getConnectionInfo()
照著官方這樣寫,似乎還是無法取得連線資料,須加上 FLAG_INCLUDE_LOCATION_INFO 才行。
WifiManager wifiManager = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
final NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
ConnectivityManager connectivityManager = activity.getSystemService(ConnectivityManager.class);
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onAvailable(Network network) {}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
String aConnectedWifiSSD = wifiInfo.getSSID().toLowerCase();
Log.d(TAG,"wifiInfo="+wifiInfo);
connectivityManager.unregisterNetworkCallback( this);
}
};
if( wifiManager.isWifiEnabled() ) {
connectivityManager.requestNetwork(request, networkCallback); // For request
connectivityManager.registerNetworkCallback(request, networkCallback); // For listen
}
# 需要的權限
android.Manifest.permission#ACCESS_FINE_LOCATION
android.Manifest.permission#ACCESS_WIFI_STATE
開發android都知道,要顯示視窗訊息,不是那直覺方便的,需要搭配Handler參與才能走得順。
# 原本以為myAlterDialog.show()後,就可以馬上執行其他程式商業邏輯,完成後停掉myAlterDialog即可
# (例如我們要實現APP「顯示"請稍後",然後背景執行其他程式商業邏輯,執行完取消"請稍後"」)
# 但我沒想到程式商業邏輯需要寫在Handler裡面才不會讓卡住。
public class MainActivity extends AppCompatActivity {
AlertDialog.Builder waitBuilder = null;
AlertDialog waitDialog;
...
protected void onCreate(Bundle savedInstanceState) {
...
waitBuilder = new AlertDialog.Builder(activity);
waitBuilder.setCancelable(false);
waitBuilder.setView(R.layout.loading_dialog);
myAlterDialog = waitBuilder.create();
}
public void onStart() {
adapter.setOnItemLongClickListener(.....){
...
runOnUiThread(new Runnable() {
@Override
public void run() {
myAlterDialog.show();
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
...
Toast.makeText(activity,"success"....
myAlterDialog.dismiss();
}
});
}
}, 300);
}
}
...
});
});
}
2011年第一隻android app有數萬下載量,到2019年我又練習寫了一個「GIT文字筆記」,是個「麻瓜勿擾的APP」。
該APP有開源,久了我也幾乎忘了有這,最近收到app要改目標版本,否則要下架,於是我更新時,再加碼限制android 13或以上才能安裝使用;
果然幾乎沒人用,到目前只有50+下載 XD
我這APP也很奇葩,用git shallow減少clone下載版本量呢。
https://play.google.com/store/apps/details?id=inmethod.gitnotetaking
後來我才知道 Android 10 (API Level 29) 已經預告取得外部檔案真實路徑的方式已經改
好險,有神人寫了好用的程式能「取得檔案的絕對路徑」,並且在 stackoverflow 說明原由,使用方式也超級方便的,請參找此神人的程式 https://github.com/HBiSoft/PickiT,一定不會失望而歸的。
公司希望Android撰寫的測試程式移植到windows平台上,但是這樣做很耗時,程式要重新開發; 因此我想到讓筆電或電腦桌機能夠直接執行android, 這樣就能解決問題了。
我排除已知的解決方案之一-Android模擬器、windows 11模擬andorid不考慮(因為要停用了)。
最後我希望類似chrome book 使用chrome OS直接支援android 的方式,沒想到還真的找到了 –FydeOS
安裝FydeOS有些眉角要注意的:
真真氣死人,也不知什麼原因,搞了快兩天,後來發現不要減肥,上架就ok了
後續我推測使用自己的java library,系統納入時判斷錯誤,刪去一些應該納入的函式,導致使用時出現invoke virtual method錯誤
minifyEnabled false
shrinkResources false
Wireguard 與最近大熱門的ChatGPT一樣, 是邪惡的存在, 以往公司網管還能勉強管控員工自架VPN, P2P, 與遠端連線, 雖說辛苦,但員工相關網路行為終究掌握在手中.
現在不得了了, 使用wireguard技術的 Tailscale 已攔不住, 隨隨便便就能翻牆, 穿透內網. 我們網管就像是鯀, 治水用阻擋的方式不再可行 , 該是要全面開放, 採用全面零信任的思維+員工契約進行資通安全的管控.
“新暗網時代” , 用wireguard, 能讓一個小群體, 自成一個暗網, 也可自行打造VPN暗黑王國, 市面上網紅推薦的乾爹乾爸VPN, 未來亦將勢微.
本文著重在翻牆, 與簽入公司內部網路, 若用來做site to site請參考以下 tailscale 建議, 直接用Wireguard 比較好.
Using WireGuard directly offers better performance than using Tailscale. Tailscale does more than WireGuard, so that will always be true. We aim to minimize that gap, and Tailscale generally offers good bandwidth and excellent latency, particularly compared to non-WireGuard VPNs.
環境
架設Headscale+UI
# 存放headscale設定檔按
mkdir -p /root/headscale/config
mkdir -p /root/headscale/data
# 虛擬網路
docker network create reverseproxy-nw
vi /root/headscale/docker-compose.yml
###############################################################################
version: '3.5'
services:
headscale:
image: headscale/headscale:0.21
container_name: headscale
volumes:
- '~/config:/etc/headscale'
- '~/data:/var/lib/headscale'
ports:
- 8081:8080
- 50443:50443
command: headscale serve
restart: unless-stopped
networks:
reverseproxy-nw:
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
restart: unless-stopped
container_name: headscale-ui
ports:
- 9443:443
networks:
reverseproxy-nw:
networks:
reverseproxy-nw:
external: true
volumes:
data:
driver: local
config:
driver: local
###############################################################################
wget https://github.com/juanfont/headscale/raw/main/config-example.yaml -O /root/headscale/config/config.yaml
# 下載完畢, 修改yaml檔案中以下參數
server_url: https://headscale.test.com
listen_addr: 0.0.0.0:8080
base_domain: test.com
#是否改寫用戶端dns,請自行測試, 依照需求決定
override_local_dns: true
# 提供(expose)給用戶端dns
nameservers:
- 1.1.1.1
- 8.8.4.4
# 公司內部專用網址,可分別參考不同dns
restricted_nameservers:
test.com:
- 192.168.1.1
# 停用magic dns
magic_dns: false
#其他如derp, 有機會再另外寫文章
cd /root/headscale
docker-compose up -d
<VirtualHost *:443>
ServerName headscale.test.com
ErrorLog /var/log/httpd/ssl_error_log
TransferLog /var/log/httpd/ssl_access_log
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/headscale.test.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/headscale.test.com/privkey.pem
SSLCACertificateFile /etc/letsencrypt//live/headscale.test.com/fullchain.pem
SSLProxyEngine On
ProxyPreserveHost On
Header add Access-Control-Allow-Origin "test.com";
SSLProxyCheckPeerName off
ProxyPass /web https://localhost:9443/web
ProxyPassReverse /web https://localhost:9443/web
ProxyPass / http://localhost:8081/ upgrade=any
ProxyPassReverse / http://localhost:8081/ upgrade=any
</VirtualHost>
https://headscale.test.com/windows
docker exec -it headscale headscale apikey create
# 建立使用者
# docker exec headscale headscale users create <使用者>
docker exec headscale headscale users create hlmtvpn
# 產生authkey, 讓tailscale端點使用
docker exec headscale headscale --user hlmtvpn preauthkeys create --reusable --expiration 24h
1. 列出 tailscale 端點
docker exec headscale headscale node list
2. 刪除 tailscale 端點
docker exec headscale headscale node delete -i <ID>
docker exec headscale headscale node delete -i 1
3. 列出 tailsacle 路由
docker exec headscale headscale route list
4. 啟用路由功能
docker exec headscale headscale route enable -r <ID>
5. 刪除路由表
docker exec headscale headscale route delete -r <ID> --force
公司架設tailscale 端點
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt-get update
sudo apt-get install tailscale
# http://headscale.test.com:8080是 headscale對外的URL
# d72e7351e24bbd21ecc99bbf5004c3e1f7cffa0631f8f5d5 是 authkey
# nat , 翻牆
tailscale up --reset --accept-routes --advertise-exit-node --snat-subnet-routes=true --accept-dns=false --advertise-routes=192.168.1.0/24 --login-server=https://headscale.test.com --authkey=d72e7351e24bbd21ecc99bbf5004c3e1f7cffa0631f8f5d5
# site to site , 不翻牆
tailscale up --reset --accept-routes --snat-subnet-routes=false --accept-dns=false --advertise-routes=192.168.1.0/24 --login-server=https://headscale.hlmt.com.tw --authkey=d72e7351e24bbd21ecc99bbf5004c3e1f7cffa0631f8f5d5
# 主機為vp, 不需要對外ip, 只要內網ip, 可上網, 要加上forward功能
# https://tailscale.com/kb/1019/subnets/
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
#列出路由表
docker exec headscale headscale route list
#啟用路由
docker exec headscale headscale route enable -r <ID>
公司主要路由(router), 須額外設定靜態路由
若需要做到site to site
PC 用戶端安裝tailscale端點(可翻牆)
cd C:\Program Files\Tailscale
tailscale login --login-server https://headscale.test.com --exit-node=<headscale ip 可為headscale server local ip> --exit-node-allow-lan-access=true
此時網頁會出現一串指令, 請將該node key 給管理人員註冊
**這邊要注意的是網址指令裡面的 USERNAME , 是一開始 headscale 新增的使用者帳號.
# 管理人員到headscale主機註冊
docker exec headscale headscale nodes register --user hlmtvpn --key nodekey:32a95789aa71c2dc7ccfa43a16cf1cff575e318c0a67117370ec9c847ff76828
若管理人員註冊完成, PC上面的tailscale會自動登入, 代表註冊成功, 此時可以選擇變身成公司tailscale 端點(如下圖 william-standard-pc-i440fx-piix-1996) , 就可以以公司ip的名義上網翻牆, 當然也可也存取公司內網(tiptop erp不行).
Android
3年前我實作一個Git文字筆記app, 使用jgit套件, 可將github clone到android手機上, 然後進行編輯, 編輯完畢,直接版控到github上面,
最近play store要求target api在2022年11月前, 至少要設定31 , 於是我就編輯了一下, 實在有夠麻煩, 一大堆套件要升級, android studio也是大改版要求舊的專案也要跟著升級.
升級升級, 想說之前jgit只使用4.5.7版本, 趁這機會,改用jgit 6.3.x版, 後來發現6.3.x 使用outputstream類別中的一個函式”transferTo”, 造成android app 找不到該method,程式跳掉.
最後我發現 android 13才支援”transferTo”
另外我開發的這支app, 當初硬改成可以接受未正式簽章的SSL網站, 現在變成資安問題,上架失敗, 原因是X509相關類別,被我繼承改成一律安全 , 只好再改成原本的檢查方式,
也就是說以後這支app要使用遠端git網站, 必須是正式簽章HTTPS, 否則是不能使用的.
為何我的app要使用jgit 6.3.x , 因為jgit終於支援shallow(depth)功能, 可以讓我clone的時候快一點, 不然有些commit數量太多, 要clone很久,很麻煩, 而app本身只想用來做文字筆記, 不用clone太多commit版本下來,不然使用者會嫌棄的.
公司有pixel手機, 趕緊升級到android 13
java
public static boolean isPlayStoreInstalled(Context context){
try {
context.getPackageManager()
.getPackageInfo(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
kotlin
fun isPlayStoreInstalled(context: Context): Boolean {
return try {
context.packageManager
.getPackageInfo(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
build.gradle
implementation 'com.google.android.gms:play-services-base:18.0.1'