diff --git a/app/src/main/java/com/juhabi/birds/custom/AddDevicePopupWindow.kt b/app/src/main/java/com/juhabi/birds/custom/AddDevicePopupWindow.kt new file mode 100644 index 0000000..dd350ec --- /dev/null +++ b/app/src/main/java/com/juhabi/birds/custom/AddDevicePopupWindow.kt @@ -0,0 +1,59 @@ +package com.juhabi.birds.custom + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.LayoutInflater +import android.view.View +import android.widget.PopupWindow +import com.juhabi.birds.R + +/** 添加设备菜单,提供手动添加和扫一扫两个操作回调。 */ +class AddDevicePopupWindow(context: Context) { + private val popupWindow: PopupWindow + + init { + val content = LayoutInflater.from(context).inflate(R.layout.popup_add_device, null) + // 让按下态绿色能裁切到卡片圆角内 + content.clipToOutline = true + popupWindow = PopupWindow( + content, + dp(context, 142), + dp(context, 84), + true + ).apply { + isOutsideTouchable = true + elevation = dp(context, 6).toFloat() + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + content.findViewById(R.id.action_manual_add).setOnClickListener { + popupWindow.dismiss() + manualAddAction?.invoke() + } + content.findViewById(R.id.action_scan).setOnClickListener { + popupWindow.dismiss() + scanAction?.invoke() + } + } + + private var manualAddAction: (() -> Unit)? = null + private var scanAction: (() -> Unit)? = null + + /** 在指定锚点下方显示菜单,并设置两个操作的回调。 */ + fun show( + anchor: View, + onManualAdd: () -> Unit, + onScan: () -> Unit + ) { + manualAddAction = onManualAdd + scanAction = onScan + popupWindow.showAsDropDown( + anchor, + -dp(anchor.context, 104), + -dp(anchor.context, 4) + ) + } + + private fun dp(context: Context, value: Int): Int = + (value * context.resources.displayMetrics.density).toInt() +} diff --git a/app/src/main/res/color/popup_add_item_text.xml b/app/src/main/res/color/popup_add_item_text.xml new file mode 100644 index 0000000..653e4c1 --- /dev/null +++ b/app/src/main/res/color/popup_add_item_text.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_confirm_dialog.xml b/app/src/main/res/drawable/bg_confirm_dialog.xml new file mode 100644 index 0000000..0056c50 --- /dev/null +++ b/app/src/main/res/drawable/bg_confirm_dialog.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_country_dialog.xml b/app/src/main/res/drawable/bg_country_dialog.xml new file mode 100644 index 0000000..5b00573 --- /dev/null +++ b/app/src/main/res/drawable/bg_country_dialog.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_popup_add_device.xml b/app/src/main/res/drawable/bg_popup_add_device.xml new file mode 100644 index 0000000..5243140 --- /dev/null +++ b/app/src/main/res/drawable/bg_popup_add_device.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_popup_add_item.xml b/app/src/main/res/drawable/bg_popup_add_item.xml new file mode 100644 index 0000000..361e468 --- /dev/null +++ b/app/src/main/res/drawable/bg_popup_add_item.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/icon_input_index.xml b/app/src/main/res/drawable/icon_input_index.xml new file mode 100644 index 0000000..9cb00fa --- /dev/null +++ b/app/src/main/res/drawable/icon_input_index.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/app/src/main/res/drawable/icon_scan_index.xml b/app/src/main/res/drawable/icon_scan_index.xml new file mode 100644 index 0000000..07222eb --- /dev/null +++ b/app/src/main/res/drawable/icon_scan_index.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/app/src/main/res/layout/dialog_country_code.xml b/app/src/main/res/layout/dialog_country_code.xml new file mode 100644 index 0000000..db30e86 --- /dev/null +++ b/app/src/main/res/layout/dialog_country_code.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/app/src/main/res/layout/item_country_code.xml b/app/src/main/res/layout/item_country_code.xml new file mode 100644 index 0000000..8e00609 --- /dev/null +++ b/app/src/main/res/layout/item_country_code.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/app/src/main/res/layout/popup_add_device.xml b/app/src/main/res/layout/popup_add_device.xml new file mode 100644 index 0000000..e4d959c --- /dev/null +++ b/app/src/main/res/layout/popup_add_device.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + diff --git a/tools/GenerateCountryCallingCodeLocales.java b/tools/GenerateCountryCallingCodeLocales.java new file mode 100644 index 0000000..7b9390b --- /dev/null +++ b/tools/GenerateCountryCallingCodeLocales.java @@ -0,0 +1,51 @@ +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Regenerates localized country names while preserving the maintained dialing codes. */ +public final class GenerateCountryCallingCodeLocales { + private static final Pattern ITEM = Pattern.compile( + "\\{\\\"code\\\":\\\"([^\\\"]+)\\\",\\\"name\\\":\\\"[^\\\"]*\\\",\\\"dial_code\\\":\\\"([^\\\"]+)\\\"}"); + + private record Country(String code, String dialCode) {} + + public static void main(String[] args) throws IOException { + Path assets = Path.of("app", "src", "main", "assets"); + String source = Files.readString(assets.resolve("country_calling_codes.json"), StandardCharsets.UTF_8); + Matcher matcher = ITEM.matcher(source); + List countries = new ArrayList<>(); + while (matcher.find()) { + countries.add(new Country(matcher.group(1), matcher.group(2))); + } + if (countries.isEmpty()) { + throw new IllegalStateException("No country records found in the source JSON"); + } + + write(assets.resolve("country_calling_codes_en.json"), countries, Locale.ENGLISH); + write(assets.resolve("country_calling_codes_ar.json"), countries, Locale.forLanguageTag("ar")); + } + + private static void write(Path output, List countries, Locale locale) throws IOException { + StringBuilder json = new StringBuilder("[\n"); + for (int index = 0; index < countries.size(); index++) { + Country country = countries.get(index); + String name = new Locale.Builder().setRegion(country.code()).build().getDisplayCountry(locale); + json.append(" {\"code\":\"").append(country.code()) + .append("\",\"name\":\"").append(escape(name)) + .append("\",\"dial_code\":\"").append(country.dialCode()).append("\"}"); + json.append(index + 1 == countries.size() ? "\n" : ",\n"); + } + json.append("]\n"); + Files.writeString(output, json, StandardCharsets.UTF_8); + } + + private static String escape(String value) { + return value.replace("\\", "\\\\").replace("\"", "\\\""); + } +}