manger: fmt code
This commit is contained in:
@@ -1,572 +0,0 @@
|
|||||||
package com.sukisu.ultra.ui.util;
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2009 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.text.Collator;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object to convert Chinese character to its corresponding pinyin string. For characters with
|
|
||||||
* multiple possible pinyin string, only one is selected according to collator. Polyphone is not
|
|
||||||
* supported in this implementation. This class is implemented to achieve the best runtime
|
|
||||||
* performance and minimum runtime resources with tolerable sacrifice of accuracy. This
|
|
||||||
* implementation highly depends on zh_CN ICU collation data and must be always synchronized with
|
|
||||||
* ICU.
|
|
||||||
* <p>
|
|
||||||
* Currently this file is aligned to zh.txt in ICU 4.6
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("SizeReplaceableByIsEmpty")
|
|
||||||
public record HanziToPinyin(boolean mHasChinaCollator) {
|
|
||||||
private static final String TAG = "HanziToPinyin";
|
|
||||||
|
|
||||||
// Turn on this flag when we want to check internal data structure.
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unihans array.
|
|
||||||
* <p>
|
|
||||||
* Each unihans is the first one within same pinyin when collator is zh_CN.
|
|
||||||
*/
|
|
||||||
public static final char[] UNIHANS = {
|
|
||||||
'阿', '哎', '安', '肮', '凹', '八',
|
|
||||||
'挀', '扳', '邦', '勹', '陂', '奔',
|
|
||||||
'伻', '屄', '边', '灬', '憋', '汃',
|
|
||||||
'冫', '癶', '峬', '嚓', '偲', '参',
|
|
||||||
'仓', '撡', '冊', '嵾', '曽', '曾',
|
|
||||||
'層', '叉', '芆', '辿', '伥', '抄',
|
|
||||||
'车', '抻', '沈', '沉', '阷', '吃',
|
|
||||||
'充', '抽', '出', '欻', '揣', '巛',
|
|
||||||
'刅', '吹', '旾', '逴', '呲', '匆',
|
|
||||||
'凑', '粗', '汆', '崔', '邨', '搓',
|
|
||||||
'咑', '呆', '丹', '当', '刀', '嘚',
|
|
||||||
'扥', '灯', '氐', '嗲', '甸', '刁',
|
|
||||||
'爹', '丁', '丟', '东', '吺', '厾',
|
|
||||||
'耑', '襨', '吨', '多', '妸', '诶',
|
|
||||||
'奀', '鞥', '儿', '发', '帆', '匚',
|
|
||||||
'飞', '分', '丰', '覅', '仏', '紑',
|
|
||||||
'伕', '旮', '侅', '甘', '冈', '皋',
|
|
||||||
'戈', '给', '根', '刯', '工', '勾',
|
|
||||||
'估', '瓜', '乖', '关', '光', '归',
|
|
||||||
'丨', '呙', '哈', '咍', '佄', '夯',
|
|
||||||
'茠', '诃', '黒', '拫', '亨', '噷',
|
|
||||||
'叿', '齁', '乯', '花', '怀', '犿',
|
|
||||||
'巟', '灰', '昏', '吙', '丌', '加',
|
|
||||||
'戋', '江', '艽', '阶', '巾', '坕',
|
|
||||||
'冂', '丩', '凥', '姢', '噘', '军',
|
|
||||||
'咔', '开', '刊', '忼', '尻', '匼',
|
|
||||||
'肎', '劥', '空', '抠', '扝', '夸',
|
|
||||||
'蒯', '宽', '匡', '亏', '坤', '扩',
|
|
||||||
'垃', '来', '兰', '啷', '捞', '肋',
|
|
||||||
'勒', '崚', '刕', '俩', '奁', '良',
|
|
||||||
'撩', '列', '拎', '刢', '溜', '囖',
|
|
||||||
'龙', '瞜', '噜', '娈', '畧', '抡',
|
|
||||||
'罗', '呣', '妈', '埋', '嫚', '牤',
|
|
||||||
'猫', '么', '呅', '门', '甿', '咪',
|
|
||||||
'宀', '喵', '乜', '民', '名', '谬',
|
|
||||||
'摸', '哞', '毪', '嗯', '拏', '腉',
|
|
||||||
'囡', '囔', '孬', '疒', '娞', '恁',
|
|
||||||
'能', '妮', '拈', '嬢', '鸟', '捏',
|
|
||||||
'囜', '宁', '妞', '农', '羺', '奴',
|
|
||||||
'奻', '疟', '黁', '郍', '喔', '讴',
|
|
||||||
'妑', '拍', '眅', '乓', '抛', '呸',
|
|
||||||
'喷', '匉', '丕', '囨', '剽', '氕',
|
|
||||||
'姘', '乒', '钋', '剖', '仆', '七',
|
|
||||||
'掐', '千', '呛', '悄', '癿', '亲',
|
|
||||||
'狅', '芎', '丘', '区', '峑', '缺',
|
|
||||||
'夋', '呥', '穣', '娆', '惹', '人',
|
|
||||||
'扔', '日', '茸', '厹', '邚', '挼',
|
|
||||||
'堧', '婑', '瞤', '捼', '仨', '毢',
|
|
||||||
'三', '桒', '掻', '閪', '森', '僧',
|
|
||||||
'杀', '筛', '山', '伤', '弰', '奢',
|
|
||||||
'申', '莘', '敒', '升', '尸', '収',
|
|
||||||
'书', '刷', '衰', '闩', '双', '谁',
|
|
||||||
'吮', '说', '厶', '忪', '捜', '苏',
|
|
||||||
'狻', '夊', '孙', '唆', '他', '囼',
|
|
||||||
'坍', '汤', '夲', '忑', '熥', '剔',
|
|
||||||
'天', '旫', '帖', '厅', '囲', '偷',
|
|
||||||
'凸', '湍', '推', '吞', '乇', '穵',
|
|
||||||
'歪', '弯', '尣', '危', '昷', '翁',
|
|
||||||
'挝', '乌', '夕', '虲', '仚', '乡',
|
|
||||||
'灱', '些', '心', '星', '凶', '休',
|
|
||||||
'吁', '吅', '削', '坃', '丫', '恹',
|
|
||||||
'央', '幺', '倻', '一', '囙', '应',
|
|
||||||
'哟', '佣', '优', '扜', '囦', '曰',
|
|
||||||
'晕', '筠', '筼', '帀', '災', '兂',
|
|
||||||
'匨', '傮', '则', '贼', '怎', '増',
|
|
||||||
'扎', '捚', '沾', '张', '长', '長',
|
|
||||||
'佋', '蜇', '贞', '争', '之', '峙',
|
|
||||||
'庢', '中', '州', '朱', '抓', '拽',
|
|
||||||
'专', '妆', '隹', '宒', '卓', '乲',
|
|
||||||
'宗', '邹', '租', '钻', '厜', '尊',
|
|
||||||
'昨', '兙', '鿃', '鿄'};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pinyin array.
|
|
||||||
* <p>
|
|
||||||
* Each pinyin is corresponding to unihans of same
|
|
||||||
* offset in the unihans array.
|
|
||||||
*/
|
|
||||||
public static final byte[][] PINYINS = {
|
|
||||||
{65, 0, 0, 0, 0, 0}, {65, 73, 0, 0, 0, 0},
|
|
||||||
{65, 78, 0, 0, 0, 0}, {65, 78, 71, 0, 0, 0},
|
|
||||||
{65, 79, 0, 0, 0, 0}, {66, 65, 0, 0, 0, 0},
|
|
||||||
{66, 65, 73, 0, 0, 0}, {66, 65, 78, 0, 0, 0},
|
|
||||||
{66, 65, 78, 71, 0, 0}, {66, 65, 79, 0, 0, 0},
|
|
||||||
{66, 69, 73, 0, 0, 0}, {66, 69, 78, 0, 0, 0},
|
|
||||||
{66, 69, 78, 71, 0, 0}, {66, 73, 0, 0, 0, 0},
|
|
||||||
{66, 73, 65, 78, 0, 0}, {66, 73, 65, 79, 0, 0},
|
|
||||||
{66, 73, 69, 0, 0, 0}, {66, 73, 78, 0, 0, 0},
|
|
||||||
{66, 73, 78, 71, 0, 0}, {66, 79, 0, 0, 0, 0},
|
|
||||||
{66, 85, 0, 0, 0, 0}, {67, 65, 0, 0, 0, 0},
|
|
||||||
{67, 65, 73, 0, 0, 0}, {67, 65, 78, 0, 0, 0},
|
|
||||||
{67, 65, 78, 71, 0, 0}, {67, 65, 79, 0, 0, 0},
|
|
||||||
{67, 69, 0, 0, 0, 0}, {67, 69, 78, 0, 0, 0},
|
|
||||||
{67, 69, 78, 71, 0, 0}, {90, 69, 78, 71, 0, 0},
|
|
||||||
{67, 69, 78, 71, 0, 0}, {67, 72, 65, 0, 0, 0},
|
|
||||||
{67, 72, 65, 73, 0, 0}, {67, 72, 65, 78, 0, 0},
|
|
||||||
{67, 72, 65, 78, 71, 0}, {67, 72, 65, 79, 0, 0},
|
|
||||||
{67, 72, 69, 0, 0, 0}, {67, 72, 69, 78, 0, 0},
|
|
||||||
{83, 72, 69, 78, 0, 0}, {67, 72, 69, 78, 0, 0},
|
|
||||||
{67, 72, 69, 78, 71, 0}, {67, 72, 73, 0, 0, 0},
|
|
||||||
{67, 72, 79, 78, 71, 0}, {67, 72, 79, 85, 0, 0},
|
|
||||||
{67, 72, 85, 0, 0, 0}, {67, 72, 85, 65, 0, 0},
|
|
||||||
{67, 72, 85, 65, 73, 0}, {67, 72, 85, 65, 78, 0},
|
|
||||||
{67, 72, 85, 65, 78, 71}, {67, 72, 85, 73, 0, 0},
|
|
||||||
{67, 72, 85, 78, 0, 0}, {67, 72, 85, 79, 0, 0},
|
|
||||||
{67, 73, 0, 0, 0, 0}, {67, 79, 78, 71, 0, 0},
|
|
||||||
{67, 79, 85, 0, 0, 0}, {67, 85, 0, 0, 0, 0},
|
|
||||||
{67, 85, 65, 78, 0, 0}, {67, 85, 73, 0, 0, 0},
|
|
||||||
{67, 85, 78, 0, 0, 0}, {67, 85, 79, 0, 0, 0},
|
|
||||||
{68, 65, 0, 0, 0, 0}, {68, 65, 73, 0, 0, 0},
|
|
||||||
{68, 65, 78, 0, 0, 0}, {68, 65, 78, 71, 0, 0},
|
|
||||||
{68, 65, 79, 0, 0, 0}, {68, 69, 0, 0, 0, 0},
|
|
||||||
{68, 69, 78, 0, 0, 0}, {68, 69, 78, 71, 0, 0},
|
|
||||||
{68, 73, 0, 0, 0, 0}, {68, 73, 65, 0, 0, 0},
|
|
||||||
{68, 73, 65, 78, 0, 0}, {68, 73, 65, 79, 0, 0},
|
|
||||||
{68, 73, 69, 0, 0, 0}, {68, 73, 78, 71, 0, 0},
|
|
||||||
{68, 73, 85, 0, 0, 0}, {68, 79, 78, 71, 0, 0},
|
|
||||||
{68, 79, 85, 0, 0, 0}, {68, 85, 0, 0, 0, 0},
|
|
||||||
{68, 85, 65, 78, 0, 0}, {68, 85, 73, 0, 0, 0},
|
|
||||||
{68, 85, 78, 0, 0, 0}, {68, 85, 79, 0, 0, 0},
|
|
||||||
{69, 0, 0, 0, 0, 0}, {69, 73, 0, 0, 0, 0},
|
|
||||||
{69, 78, 0, 0, 0, 0}, {69, 78, 71, 0, 0, 0},
|
|
||||||
{69, 82, 0, 0, 0, 0}, {70, 65, 0, 0, 0, 0},
|
|
||||||
{70, 65, 78, 0, 0, 0}, {70, 65, 78, 71, 0, 0},
|
|
||||||
{70, 69, 73, 0, 0, 0}, {70, 69, 78, 0, 0, 0},
|
|
||||||
{70, 69, 78, 71, 0, 0}, {70, 73, 65, 79, 0, 0},
|
|
||||||
{70, 79, 0, 0, 0, 0}, {70, 79, 85, 0, 0, 0},
|
|
||||||
{70, 85, 0, 0, 0, 0}, {71, 65, 0, 0, 0, 0},
|
|
||||||
{71, 65, 73, 0, 0, 0}, {71, 65, 78, 0, 0, 0},
|
|
||||||
{71, 65, 78, 71, 0, 0}, {71, 65, 79, 0, 0, 0},
|
|
||||||
{71, 69, 0, 0, 0, 0}, {71, 69, 73, 0, 0, 0},
|
|
||||||
{71, 69, 78, 0, 0, 0}, {71, 69, 78, 71, 0, 0},
|
|
||||||
{71, 79, 78, 71, 0, 0}, {71, 79, 85, 0, 0, 0},
|
|
||||||
{71, 85, 0, 0, 0, 0}, {71, 85, 65, 0, 0, 0},
|
|
||||||
{71, 85, 65, 73, 0, 0}, {71, 85, 65, 78, 0, 0},
|
|
||||||
{71, 85, 65, 78, 71, 0}, {71, 85, 73, 0, 0, 0},
|
|
||||||
{71, 85, 78, 0, 0, 0}, {71, 85, 79, 0, 0, 0},
|
|
||||||
{72, 65, 0, 0, 0, 0}, {72, 65, 73, 0, 0, 0},
|
|
||||||
{72, 65, 78, 0, 0, 0}, {72, 65, 78, 71, 0, 0},
|
|
||||||
{72, 65, 79, 0, 0, 0}, {72, 69, 0, 0, 0, 0},
|
|
||||||
{72, 69, 73, 0, 0, 0}, {72, 69, 78, 0, 0, 0},
|
|
||||||
{72, 69, 78, 71, 0, 0}, {72, 77, 0, 0, 0, 0},
|
|
||||||
{72, 79, 78, 71, 0, 0}, {72, 79, 85, 0, 0, 0},
|
|
||||||
{72, 85, 0, 0, 0, 0}, {72, 85, 65, 0, 0, 0},
|
|
||||||
{72, 85, 65, 73, 0, 0}, {72, 85, 65, 78, 0, 0},
|
|
||||||
{72, 85, 65, 78, 71, 0}, {72, 85, 73, 0, 0, 0},
|
|
||||||
{72, 85, 78, 0, 0, 0}, {72, 85, 79, 0, 0, 0},
|
|
||||||
{74, 73, 0, 0, 0, 0}, {74, 73, 65, 0, 0, 0},
|
|
||||||
{74, 73, 65, 78, 0, 0}, {74, 73, 65, 78, 71, 0},
|
|
||||||
{74, 73, 65, 79, 0, 0}, {74, 73, 69, 0, 0, 0},
|
|
||||||
{74, 73, 78, 0, 0, 0}, {74, 73, 78, 71, 0, 0},
|
|
||||||
{74, 73, 79, 78, 71, 0}, {74, 73, 85, 0, 0, 0},
|
|
||||||
{74, 85, 0, 0, 0, 0}, {74, 85, 65, 78, 0, 0},
|
|
||||||
{74, 85, 69, 0, 0, 0}, {74, 85, 78, 0, 0, 0},
|
|
||||||
{75, 65, 0, 0, 0, 0}, {75, 65, 73, 0, 0, 0},
|
|
||||||
{75, 65, 78, 0, 0, 0}, {75, 65, 78, 71, 0, 0},
|
|
||||||
{75, 65, 79, 0, 0, 0}, {75, 69, 0, 0, 0, 0},
|
|
||||||
{75, 69, 78, 0, 0, 0}, {75, 69, 78, 71, 0, 0},
|
|
||||||
{75, 79, 78, 71, 0, 0}, {75, 79, 85, 0, 0, 0},
|
|
||||||
{75, 85, 0, 0, 0, 0}, {75, 85, 65, 0, 0, 0},
|
|
||||||
{75, 85, 65, 73, 0, 0}, {75, 85, 65, 78, 0, 0},
|
|
||||||
{75, 85, 65, 78, 71, 0}, {75, 85, 73, 0, 0, 0},
|
|
||||||
{75, 85, 78, 0, 0, 0}, {75, 85, 79, 0, 0, 0},
|
|
||||||
{76, 65, 0, 0, 0, 0}, {76, 65, 73, 0, 0, 0},
|
|
||||||
{76, 65, 78, 0, 0, 0}, {76, 65, 78, 71, 0, 0},
|
|
||||||
{76, 65, 79, 0, 0, 0}, {76, 69, 0, 0, 0, 0},
|
|
||||||
{76, 69, 73, 0, 0, 0}, {76, 69, 78, 71, 0, 0},
|
|
||||||
{76, 73, 0, 0, 0, 0}, {76, 73, 65, 0, 0, 0},
|
|
||||||
{76, 73, 65, 78, 0, 0}, {76, 73, 65, 78, 71, 0},
|
|
||||||
{76, 73, 65, 79, 0, 0}, {76, 73, 69, 0, 0, 0},
|
|
||||||
{76, 73, 78, 0, 0, 0}, {76, 73, 78, 71, 0, 0},
|
|
||||||
{76, 73, 85, 0, 0, 0}, {76, 79, 0, 0, 0, 0},
|
|
||||||
{76, 79, 78, 71, 0, 0}, {76, 79, 85, 0, 0, 0},
|
|
||||||
{76, 85, 0, 0, 0, 0}, {76, 85, 65, 78, 0, 0},
|
|
||||||
{76, 85, 69, 0, 0, 0}, {76, 85, 78, 0, 0, 0},
|
|
||||||
{76, 85, 79, 0, 0, 0}, {77, 0, 0, 0, 0, 0},
|
|
||||||
{77, 65, 0, 0, 0, 0}, {77, 65, 73, 0, 0, 0},
|
|
||||||
{77, 65, 78, 0, 0, 0}, {77, 65, 78, 71, 0, 0},
|
|
||||||
{77, 65, 79, 0, 0, 0}, {77, 69, 0, 0, 0, 0},
|
|
||||||
{77, 69, 73, 0, 0, 0}, {77, 69, 78, 0, 0, 0},
|
|
||||||
{77, 69, 78, 71, 0, 0}, {77, 73, 0, 0, 0, 0},
|
|
||||||
{77, 73, 65, 78, 0, 0}, {77, 73, 65, 79, 0, 0},
|
|
||||||
{77, 73, 69, 0, 0, 0}, {77, 73, 78, 0, 0, 0},
|
|
||||||
{77, 73, 78, 71, 0, 0}, {77, 73, 85, 0, 0, 0},
|
|
||||||
{77, 79, 0, 0, 0, 0}, {77, 79, 85, 0, 0, 0},
|
|
||||||
{77, 85, 0, 0, 0, 0}, {78, 0, 0, 0, 0, 0},
|
|
||||||
{78, 65, 0, 0, 0, 0}, {78, 65, 73, 0, 0, 0},
|
|
||||||
{78, 65, 78, 0, 0, 0}, {78, 65, 78, 71, 0, 0},
|
|
||||||
{78, 65, 79, 0, 0, 0}, {78, 69, 0, 0, 0, 0},
|
|
||||||
{78, 69, 73, 0, 0, 0}, {78, 69, 78, 0, 0, 0},
|
|
||||||
{78, 69, 78, 71, 0, 0}, {78, 73, 0, 0, 0, 0},
|
|
||||||
{78, 73, 65, 78, 0, 0}, {78, 73, 65, 78, 71, 0},
|
|
||||||
{78, 73, 65, 79, 0, 0}, {78, 73, 69, 0, 0, 0},
|
|
||||||
{78, 73, 78, 0, 0, 0}, {78, 73, 78, 71, 0, 0},
|
|
||||||
{78, 73, 85, 0, 0, 0}, {78, 79, 78, 71, 0, 0},
|
|
||||||
{78, 79, 85, 0, 0, 0}, {78, 85, 0, 0, 0, 0},
|
|
||||||
{78, 85, 65, 78, 0, 0}, {78, 85, 69, 0, 0, 0},
|
|
||||||
{78, 85, 78, 0, 0, 0}, {78, 85, 79, 0, 0, 0},
|
|
||||||
{79, 0, 0, 0, 0, 0}, {79, 85, 0, 0, 0, 0},
|
|
||||||
{80, 65, 0, 0, 0, 0}, {80, 65, 73, 0, 0, 0},
|
|
||||||
{80, 65, 78, 0, 0, 0}, {80, 65, 78, 71, 0, 0},
|
|
||||||
{80, 65, 79, 0, 0, 0}, {80, 69, 73, 0, 0, 0},
|
|
||||||
{80, 69, 78, 0, 0, 0}, {80, 69, 78, 71, 0, 0},
|
|
||||||
{80, 73, 0, 0, 0, 0}, {80, 73, 65, 78, 0, 0},
|
|
||||||
{80, 73, 65, 79, 0, 0}, {80, 73, 69, 0, 0, 0},
|
|
||||||
{80, 73, 78, 0, 0, 0}, {80, 73, 78, 71, 0, 0},
|
|
||||||
{80, 79, 0, 0, 0, 0}, {80, 79, 85, 0, 0, 0},
|
|
||||||
{80, 85, 0, 0, 0, 0}, {81, 73, 0, 0, 0, 0},
|
|
||||||
{81, 73, 65, 0, 0, 0}, {81, 73, 65, 78, 0, 0},
|
|
||||||
{81, 73, 65, 78, 71, 0}, {81, 73, 65, 79, 0, 0},
|
|
||||||
{81, 73, 69, 0, 0, 0}, {81, 73, 78, 0, 0, 0},
|
|
||||||
{81, 73, 78, 71, 0, 0}, {81, 73, 79, 78, 71, 0},
|
|
||||||
{81, 73, 85, 0, 0, 0}, {81, 85, 0, 0, 0, 0},
|
|
||||||
{81, 85, 65, 78, 0, 0}, {81, 85, 69, 0, 0, 0},
|
|
||||||
{81, 85, 78, 0, 0, 0}, {82, 65, 78, 0, 0, 0},
|
|
||||||
{82, 65, 78, 71, 0, 0}, {82, 65, 79, 0, 0, 0},
|
|
||||||
{82, 69, 0, 0, 0, 0}, {82, 69, 78, 0, 0, 0},
|
|
||||||
{82, 69, 78, 71, 0, 0}, {82, 73, 0, 0, 0, 0},
|
|
||||||
{82, 79, 78, 71, 0, 0}, {82, 79, 85, 0, 0, 0},
|
|
||||||
{82, 85, 0, 0, 0, 0}, {82, 85, 65, 0, 0, 0},
|
|
||||||
{82, 85, 65, 78, 0, 0}, {82, 85, 73, 0, 0, 0},
|
|
||||||
{82, 85, 78, 0, 0, 0}, {82, 85, 79, 0, 0, 0},
|
|
||||||
{83, 65, 0, 0, 0, 0}, {83, 65, 73, 0, 0, 0},
|
|
||||||
{83, 65, 78, 0, 0, 0}, {83, 65, 78, 71, 0, 0},
|
|
||||||
{83, 65, 79, 0, 0, 0}, {83, 69, 0, 0, 0, 0},
|
|
||||||
{83, 69, 78, 0, 0, 0}, {83, 69, 78, 71, 0, 0},
|
|
||||||
{83, 72, 65, 0, 0, 0}, {83, 72, 65, 73, 0, 0},
|
|
||||||
{83, 72, 65, 78, 0, 0}, {83, 72, 65, 78, 71, 0},
|
|
||||||
{83, 72, 65, 79, 0, 0}, {83, 72, 69, 0, 0, 0},
|
|
||||||
{83, 72, 69, 78, 0, 0}, {88, 73, 78, 0, 0, 0},
|
|
||||||
{83, 72, 69, 78, 0, 0}, {83, 72, 69, 78, 71, 0},
|
|
||||||
{83, 72, 73, 0, 0, 0}, {83, 72, 79, 85, 0, 0},
|
|
||||||
{83, 72, 85, 0, 0, 0}, {83, 72, 85, 65, 0, 0},
|
|
||||||
{83, 72, 85, 65, 73, 0}, {83, 72, 85, 65, 78, 0},
|
|
||||||
{83, 72, 85, 65, 78, 71}, {83, 72, 85, 73, 0, 0},
|
|
||||||
{83, 72, 85, 78, 0, 0}, {83, 72, 85, 79, 0, 0},
|
|
||||||
{83, 73, 0, 0, 0, 0}, {83, 79, 78, 71, 0, 0},
|
|
||||||
{83, 79, 85, 0, 0, 0}, {83, 85, 0, 0, 0, 0},
|
|
||||||
{83, 85, 65, 78, 0, 0}, {83, 85, 73, 0, 0, 0},
|
|
||||||
{83, 85, 78, 0, 0, 0}, {83, 85, 79, 0, 0, 0},
|
|
||||||
{84, 65, 0, 0, 0, 0}, {84, 65, 73, 0, 0, 0},
|
|
||||||
{84, 65, 78, 0, 0, 0}, {84, 65, 78, 71, 0, 0},
|
|
||||||
{84, 65, 79, 0, 0, 0}, {84, 69, 0, 0, 0, 0},
|
|
||||||
{84, 69, 78, 71, 0, 0}, {84, 73, 0, 0, 0, 0},
|
|
||||||
{84, 73, 65, 78, 0, 0}, {84, 73, 65, 79, 0, 0},
|
|
||||||
{84, 73, 69, 0, 0, 0}, {84, 73, 78, 71, 0, 0},
|
|
||||||
{84, 79, 78, 71, 0, 0}, {84, 79, 85, 0, 0, 0},
|
|
||||||
{84, 85, 0, 0, 0, 0}, {84, 85, 65, 78, 0, 0},
|
|
||||||
{84, 85, 73, 0, 0, 0}, {84, 85, 78, 0, 0, 0},
|
|
||||||
{84, 85, 79, 0, 0, 0}, {87, 65, 0, 0, 0, 0},
|
|
||||||
{87, 65, 73, 0, 0, 0}, {87, 65, 78, 0, 0, 0},
|
|
||||||
{87, 65, 78, 71, 0, 0}, {87, 69, 73, 0, 0, 0},
|
|
||||||
{87, 69, 78, 0, 0, 0}, {87, 69, 78, 71, 0, 0},
|
|
||||||
{87, 79, 0, 0, 0, 0}, {87, 85, 0, 0, 0, 0},
|
|
||||||
{88, 73, 0, 0, 0, 0}, {88, 73, 65, 0, 0, 0},
|
|
||||||
{88, 73, 65, 78, 0, 0}, {88, 73, 65, 78, 71, 0},
|
|
||||||
{88, 73, 65, 79, 0, 0}, {88, 73, 69, 0, 0, 0},
|
|
||||||
{88, 73, 78, 0, 0, 0}, {88, 73, 78, 71, 0, 0},
|
|
||||||
{88, 73, 79, 78, 71, 0}, {88, 73, 85, 0, 0, 0},
|
|
||||||
{88, 85, 0, 0, 0, 0}, {88, 85, 65, 78, 0, 0},
|
|
||||||
{88, 85, 69, 0, 0, 0}, {88, 85, 78, 0, 0, 0},
|
|
||||||
{89, 65, 0, 0, 0, 0}, {89, 65, 78, 0, 0, 0},
|
|
||||||
{89, 65, 78, 71, 0, 0}, {89, 65, 79, 0, 0, 0},
|
|
||||||
{89, 69, 0, 0, 0, 0}, {89, 73, 0, 0, 0, 0},
|
|
||||||
{89, 73, 78, 0, 0, 0}, {89, 73, 78, 71, 0, 0},
|
|
||||||
{89, 79, 0, 0, 0, 0}, {89, 79, 78, 71, 0, 0},
|
|
||||||
{89, 79, 85, 0, 0, 0}, {89, 85, 0, 0, 0, 0},
|
|
||||||
{89, 85, 65, 78, 0, 0}, {89, 85, 69, 0, 0, 0},
|
|
||||||
{89, 85, 78, 0, 0, 0}, {74, 85, 78, 0, 0, 0},
|
|
||||||
{89, 85, 78, 0, 0, 0}, {90, 65, 0, 0, 0, 0},
|
|
||||||
{90, 65, 73, 0, 0, 0}, {90, 65, 78, 0, 0, 0},
|
|
||||||
{90, 65, 78, 71, 0, 0}, {90, 65, 79, 0, 0, 0},
|
|
||||||
{90, 69, 0, 0, 0, 0}, {90, 69, 73, 0, 0, 0},
|
|
||||||
{90, 69, 78, 0, 0, 0}, {90, 69, 78, 71, 0, 0},
|
|
||||||
{90, 72, 65, 0, 0, 0}, {90, 72, 65, 73, 0, 0},
|
|
||||||
{90, 72, 65, 78, 0, 0}, {90, 72, 65, 78, 71, 0},
|
|
||||||
{67, 72, 65, 78, 71, 0}, {90, 72, 65, 78, 71, 0},
|
|
||||||
{90, 72, 65, 79, 0, 0}, {90, 72, 69, 0, 0, 0},
|
|
||||||
{90, 72, 69, 78, 0, 0}, {90, 72, 69, 78, 71, 0},
|
|
||||||
{90, 72, 73, 0, 0, 0}, {83, 72, 73, 0, 0, 0},
|
|
||||||
{90, 72, 73, 0, 0, 0}, {90, 72, 79, 78, 71, 0},
|
|
||||||
{90, 72, 79, 85, 0, 0}, {90, 72, 85, 0, 0, 0},
|
|
||||||
{90, 72, 85, 65, 0, 0}, {90, 72, 85, 65, 73, 0},
|
|
||||||
{90, 72, 85, 65, 78, 0}, {90, 72, 85, 65, 78, 71},
|
|
||||||
{90, 72, 85, 73, 0, 0}, {90, 72, 85, 78, 0, 0},
|
|
||||||
{90, 72, 85, 79, 0, 0}, {90, 73, 0, 0, 0, 0},
|
|
||||||
{90, 79, 78, 71, 0, 0}, {90, 79, 85, 0, 0, 0},
|
|
||||||
{90, 85, 0, 0, 0, 0}, {90, 85, 65, 78, 0, 0},
|
|
||||||
{90, 85, 73, 0, 0, 0}, {90, 85, 78, 0, 0, 0},
|
|
||||||
{90, 85, 79, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
|
|
||||||
{83, 72, 65, 78, 0, 0}, {0, 0, 0, 0, 0, 0}};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First and last Chinese character with known Pinyin according to zh collation
|
|
||||||
*/
|
|
||||||
private static final String FIRST_PINYIN_UNIHAN = "阿";
|
|
||||||
private static final String LAST_PINYIN_UNIHAN = "鿿";
|
|
||||||
|
|
||||||
private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
|
|
||||||
|
|
||||||
private static HanziToPinyin sInstance;
|
|
||||||
|
|
||||||
public static class Token {
|
|
||||||
/**
|
|
||||||
* Separator between target string for each source char
|
|
||||||
*/
|
|
||||||
public static final String SEPARATOR = " ";
|
|
||||||
|
|
||||||
public static final int LATIN = 1;
|
|
||||||
public static final int PINYIN = 2;
|
|
||||||
public static final int UNKNOWN = 3;
|
|
||||||
|
|
||||||
public Token() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Token(int type, String source, String target) {
|
|
||||||
this.type = type;
|
|
||||||
this.source = source;
|
|
||||||
this.target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of this token, ASCII, PINYIN or UNKNOWN.
|
|
||||||
*/
|
|
||||||
public int type;
|
|
||||||
/**
|
|
||||||
* Original string before translation.
|
|
||||||
*/
|
|
||||||
public String source;
|
|
||||||
/**
|
|
||||||
* Translated string of source. For Han, target is corresponding Pinyin. Otherwise target is
|
|
||||||
* original string in source.
|
|
||||||
*/
|
|
||||||
public String target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HanziToPinyin getInstance() {
|
|
||||||
synchronized (HanziToPinyin.class) {
|
|
||||||
if (sInstance != null) {
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
// Check if zh_CN collation data is available
|
|
||||||
final Locale[] locale = Collator.getAvailableLocales();
|
|
||||||
for (Locale value : locale) {
|
|
||||||
if (value.equals(Locale.CHINA) || value.getLanguage().contains("zh")) {
|
|
||||||
// Do self validation just once.
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Self validation. Result: " + doSelfValidation());
|
|
||||||
}
|
|
||||||
sInstance = new HanziToPinyin(true);
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sInstance == null) {//这个判断是用于处理国产ROM的兼容性问题
|
|
||||||
if (Locale.CHINA.equals(Locale.getDefault())) {
|
|
||||||
sInstance = new HanziToPinyin(true);
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled");
|
|
||||||
sInstance = new HanziToPinyin(false);
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate if our internal table has some wrong value.
|
|
||||||
*
|
|
||||||
* @return true when the table looks correct.
|
|
||||||
*/
|
|
||||||
private static boolean doSelfValidation() {
|
|
||||||
char lastChar = UNIHANS[0];
|
|
||||||
String lastString = Character.toString(lastChar);
|
|
||||||
for (char c : UNIHANS) {
|
|
||||||
if (lastChar == c) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String curString = Character.toString(c);
|
|
||||||
int cmp = COLLATOR.compare(lastString, curString);
|
|
||||||
if (cmp >= 0) {
|
|
||||||
Log.e(TAG, "Internal error in Unihan table. " + "The last string \"" + lastString
|
|
||||||
+ "\" is greater than current string \"" + curString + "\".");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
lastString = curString;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Token getToken(char character) {
|
|
||||||
Token token = new Token();
|
|
||||||
final String letter = Character.toString(character);
|
|
||||||
token.source = letter;
|
|
||||||
int offset = -1;
|
|
||||||
int cmp;
|
|
||||||
if (character < 256) {
|
|
||||||
token.type = Token.LATIN;
|
|
||||||
token.target = letter;
|
|
||||||
return token;
|
|
||||||
} else {
|
|
||||||
cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN);
|
|
||||||
if (cmp < 0) {
|
|
||||||
token.type = Token.UNKNOWN;
|
|
||||||
token.target = letter;
|
|
||||||
return token;
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
token.type = Token.PINYIN;
|
|
||||||
offset = 0;
|
|
||||||
} else {
|
|
||||||
cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN);
|
|
||||||
if (cmp > 0) {
|
|
||||||
token.type = Token.UNKNOWN;
|
|
||||||
token.target = letter;
|
|
||||||
return token;
|
|
||||||
} else if (cmp == 0) {
|
|
||||||
token.type = Token.PINYIN;
|
|
||||||
offset = UNIHANS.length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
token.type = Token.PINYIN;
|
|
||||||
if (offset < 0) {
|
|
||||||
int begin = 0;
|
|
||||||
int end = UNIHANS.length - 1;
|
|
||||||
while (begin <= end) {
|
|
||||||
offset = (begin + end) / 2;
|
|
||||||
final String unihan = Character.toString(UNIHANS[offset]);
|
|
||||||
cmp = COLLATOR.compare(letter, unihan);
|
|
||||||
if (cmp == 0) {
|
|
||||||
break;
|
|
||||||
} else if (cmp > 0) {
|
|
||||||
begin = offset + 1;
|
|
||||||
} else {
|
|
||||||
end = offset - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmp < 0) {
|
|
||||||
offset--;
|
|
||||||
}
|
|
||||||
StringBuilder pinyin = new StringBuilder();
|
|
||||||
for (int j = 0; j < PINYINS[offset].length && PINYINS[offset][j] != 0; j++) {
|
|
||||||
pinyin.append((char) PINYINS[offset][j]);
|
|
||||||
}
|
|
||||||
token.target = pinyin.toString();
|
|
||||||
if (TextUtils.isEmpty(token.target)) {
|
|
||||||
token.type = Token.UNKNOWN;
|
|
||||||
token.target = token.source;
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the input to a array of tokens. The sequence of ASCII or Unknown characters without
|
|
||||||
* space will be put into a Token, One Hanzi character which has pinyin will be treated as a
|
|
||||||
* Token. If these is no China collator, the empty token array is returned.
|
|
||||||
*/
|
|
||||||
public ArrayList<Token> get(final String input) {
|
|
||||||
ArrayList<Token> tokens = new ArrayList<>();
|
|
||||||
if (!mHasChinaCollator || TextUtils.isEmpty(input)) {
|
|
||||||
// return empty tokens.
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
final int inputLength = input.length();
|
|
||||||
final StringBuilder sb = new StringBuilder();
|
|
||||||
int tokenType = Token.LATIN;
|
|
||||||
// Go through the input, create a new token when
|
|
||||||
// a. Token type changed
|
|
||||||
// b. Get the Pinyin of current charater.
|
|
||||||
// c. current character is space.
|
|
||||||
for (int i = 0; i < inputLength; i++) {
|
|
||||||
final char character = input.charAt(i);
|
|
||||||
if (character == ' ') {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
addToken(sb, tokens, tokenType);
|
|
||||||
}
|
|
||||||
} else if (character < 256) {
|
|
||||||
if (tokenType != Token.LATIN && sb.length() > 0) {
|
|
||||||
addToken(sb, tokens, tokenType);
|
|
||||||
}
|
|
||||||
tokenType = Token.LATIN;
|
|
||||||
sb.append(character);
|
|
||||||
} else {
|
|
||||||
Token t = getToken(character);
|
|
||||||
if (t.type == Token.PINYIN) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
addToken(sb, tokens, tokenType);
|
|
||||||
}
|
|
||||||
tokens.add(t);
|
|
||||||
tokenType = Token.PINYIN;
|
|
||||||
} else {
|
|
||||||
if (tokenType != t.type && sb.length() > 0) {
|
|
||||||
addToken(sb, tokens, tokenType);
|
|
||||||
}
|
|
||||||
tokenType = t.type;
|
|
||||||
sb.append(character);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
addToken(sb, tokens, tokenType);
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addToken(
|
|
||||||
final StringBuilder sb, final ArrayList<Token> tokens, final int tokenType) {
|
|
||||||
String str = sb.toString();
|
|
||||||
tokens.add(new Token(tokenType, str, str));
|
|
||||||
sb.setLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toPinyinString(String string) {
|
|
||||||
if (string == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
ArrayList<Token> tokens = get(string);
|
|
||||||
for (Token token : tokens) {
|
|
||||||
sb.append(token.target);
|
|
||||||
}
|
|
||||||
return sb.toString().toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,522 @@
|
|||||||
|
package com.sukisu.ultra.ui.util
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import java.text.Collator
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class HanziToPinyin private constructor(val hasChinaCollator: Boolean) {
|
||||||
|
|
||||||
|
class Token(
|
||||||
|
var type: Int = 0,
|
||||||
|
var source: String = "",
|
||||||
|
var target: String = ""
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
const val LATIN = 1
|
||||||
|
const val PINYIN = 2
|
||||||
|
const val UNKNOWN = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getToken(character: Char): Token {
|
||||||
|
val token = Token()
|
||||||
|
val letter = character.toString()
|
||||||
|
token.source = letter
|
||||||
|
var offset = -1
|
||||||
|
var cmp: Int
|
||||||
|
|
||||||
|
if (character < 256.toChar()) {
|
||||||
|
token.type = Token.LATIN
|
||||||
|
token.target = letter
|
||||||
|
return token
|
||||||
|
} else {
|
||||||
|
cmp = COLLATOR.compare(letter, FIRST_PINYIN_UNIHAN)
|
||||||
|
if (cmp < 0) {
|
||||||
|
token.type = Token.UNKNOWN
|
||||||
|
token.target = letter
|
||||||
|
return token
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
token.type = Token.PINYIN
|
||||||
|
offset = 0
|
||||||
|
} else {
|
||||||
|
cmp = COLLATOR.compare(letter, LAST_PINYIN_UNIHAN)
|
||||||
|
if (cmp > 0) {
|
||||||
|
token.type = Token.UNKNOWN
|
||||||
|
token.target = letter
|
||||||
|
return token
|
||||||
|
} else if (cmp == 0) {
|
||||||
|
token.type = Token.PINYIN
|
||||||
|
offset = UNIHANS.size - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token.type = Token.PINYIN
|
||||||
|
if (offset < 0) {
|
||||||
|
var begin = 0
|
||||||
|
var end = UNIHANS.size - 1
|
||||||
|
while (begin <= end) {
|
||||||
|
offset = (begin + end) / 2
|
||||||
|
val unihan = UNIHANS[offset].toString()
|
||||||
|
cmp = COLLATOR.compare(letter, unihan)
|
||||||
|
when {
|
||||||
|
cmp == 0 -> break
|
||||||
|
cmp > 0 -> begin = offset + 1
|
||||||
|
else -> end = offset - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cmp < 0) {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
|
||||||
|
val pinyin = StringBuilder()
|
||||||
|
for (j in PINYINS[offset].indices) {
|
||||||
|
if (PINYINS[offset][j] == 0.toByte()) break
|
||||||
|
pinyin.append(PINYINS[offset][j].toInt().toChar())
|
||||||
|
}
|
||||||
|
token.target = pinyin.toString()
|
||||||
|
if (TextUtils.isEmpty(token.target)) {
|
||||||
|
token.type = Token.UNKNOWN
|
||||||
|
token.target = token.source
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
fun get(input: String?): ArrayList<Token> {
|
||||||
|
val tokens = ArrayList<Token>()
|
||||||
|
if (!hasChinaCollator || TextUtils.isEmpty(input)) {
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
val inputLength = input!!.length
|
||||||
|
val sb = StringBuilder()
|
||||||
|
var tokenType = Token.LATIN
|
||||||
|
|
||||||
|
for (i in 0 until inputLength) {
|
||||||
|
val character = input[i]
|
||||||
|
when {
|
||||||
|
character == ' ' -> {
|
||||||
|
if (sb.isNotEmpty()) {
|
||||||
|
addToken(sb, tokens, tokenType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
character < 256.toChar() -> {
|
||||||
|
if (tokenType != Token.LATIN && sb.isNotEmpty()) {
|
||||||
|
addToken(sb, tokens, tokenType)
|
||||||
|
}
|
||||||
|
tokenType = Token.LATIN
|
||||||
|
sb.append(character)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val t = getToken(character)
|
||||||
|
if (t.type == Token.PINYIN) {
|
||||||
|
if (sb.isNotEmpty()) {
|
||||||
|
addToken(sb, tokens, tokenType)
|
||||||
|
}
|
||||||
|
tokens.add(t)
|
||||||
|
tokenType = Token.PINYIN
|
||||||
|
} else {
|
||||||
|
if (tokenType != t.type && sb.isNotEmpty()) {
|
||||||
|
addToken(sb, tokens, tokenType)
|
||||||
|
}
|
||||||
|
tokenType = t.type
|
||||||
|
sb.append(character)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb.isNotEmpty()) {
|
||||||
|
addToken(sb, tokens, tokenType)
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addToken(sb: StringBuilder, tokens: ArrayList<Token>, tokenType: Int) {
|
||||||
|
val str = sb.toString()
|
||||||
|
tokens.add(Token(tokenType, str, str))
|
||||||
|
sb.setLength(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toPinyinString(string: String?): String? {
|
||||||
|
if (string == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val tokens = get(string)
|
||||||
|
for (token in tokens) {
|
||||||
|
sb.append(token.target)
|
||||||
|
}
|
||||||
|
return sb.toString().lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "HanziToPinyin"
|
||||||
|
private const val DEBUG = false
|
||||||
|
|
||||||
|
val UNIHANS = charArrayOf(
|
||||||
|
'阿', '哎', '安', '肮', '凹', '八',
|
||||||
|
'挀', '扳', '邦', '勹', '陂', '奔',
|
||||||
|
'伻', '屄', '边', '灬', '憋', '汃',
|
||||||
|
'冫', '癶', '峬', '嚓', '偲', '参',
|
||||||
|
'仓', '撡', '冊', '嵾', '曽', '曾',
|
||||||
|
'層', '叉', '芆', '辿', '伥', '抄',
|
||||||
|
'车', '抻', '沈', '沉', '阷', '吃',
|
||||||
|
'充', '抽', '出', '欻', '揣', '巛',
|
||||||
|
'刅', '吹', '旾', '逴', '呲', '匆',
|
||||||
|
'凑', '粗', '汆', '崔', '邨', '搓',
|
||||||
|
'咑', '呆', '丹', '当', '刀', '嘚',
|
||||||
|
'扥', '灯', '氐', '嗲', '甸', '刁',
|
||||||
|
'爹', '丁', '丟', '东', '吺', '厾',
|
||||||
|
'耑', '襨', '吨', '多', '妸', '诶',
|
||||||
|
'奀', '鞥', '儿', '发', '帆', '匚',
|
||||||
|
'飞', '分', '丰', '覅', '仏', '紑',
|
||||||
|
'伕', '旮', '侅', '甘', '冈', '皋',
|
||||||
|
'戈', '给', '根', '刯', '工', '勾',
|
||||||
|
'估', '瓜', '乖', '关', '光', '归',
|
||||||
|
'丨', '呙', '哈', '咍', '佄', '夯',
|
||||||
|
'茠', '诃', '黒', '拫', '亨', '噷',
|
||||||
|
'叿', '齁', '乯', '花', '怀', '犿',
|
||||||
|
'巟', '灰', '昏', '吙', '丌', '加',
|
||||||
|
'戋', '江', '艽', '阶', '巾', '坕',
|
||||||
|
'冂', '丩', '凥', '姢', '噘', '军',
|
||||||
|
'咔', '开', '刊', '忼', '尻', '匼',
|
||||||
|
'肎', '劥', '空', '抠', '扝', '夸',
|
||||||
|
'蒯', '宽', '匡', '亏', '坤', '扩',
|
||||||
|
'垃', '来', '兰', '啷', '捞', '肋',
|
||||||
|
'勒', '崚', '刕', '俩', '奁', '良',
|
||||||
|
'撩', '列', '拎', '刢', '溜', '囖',
|
||||||
|
'龙', '瞜', '噜', '娈', '畧', '抡',
|
||||||
|
'罗', '呣', '妈', '埋', '嫚', '牤',
|
||||||
|
'猫', '么', '呅', '门', '甿', '咪',
|
||||||
|
'宀', '喵', '乜', '民', '名', '谬',
|
||||||
|
'摸', '哞', '毪', '嗯', '拏', '腉',
|
||||||
|
'囡', '囔', '孬', '疒', '娞', '恁',
|
||||||
|
'能', '妮', '拈', '嬢', '鸟', '捏',
|
||||||
|
'囜', '宁', '妞', '农', '羺', '奴',
|
||||||
|
'奻', '疟', '黁', '郍', '喔', '讴',
|
||||||
|
'妑', '拍', '眅', '乓', '抛', '呸',
|
||||||
|
'喷', '匉', '丕', '囨', '剽', '氕',
|
||||||
|
'姘', '乒', '钋', '剖', '仆', '七',
|
||||||
|
'掐', '千', '呛', '悄', '癿', '亲',
|
||||||
|
'狅', '芎', '丘', '区', '峑', '缺',
|
||||||
|
'夋', '呥', '穣', '娆', '惹', '人',
|
||||||
|
'扔', '日', '茸', '厹', '邚', '挼',
|
||||||
|
'堧', '婑', '瞤', '捼', '仨', '毢',
|
||||||
|
'三', '桒', '掻', '閪', '森', '僧',
|
||||||
|
'杀', '筛', '山', '伤', '弰', '奢',
|
||||||
|
'申', '莘', '敒', '升', '尸', '収',
|
||||||
|
'书', '刷', '衰', '闩', '双', '谁',
|
||||||
|
'吮', '说', '厶', '忪', '捜', '苏',
|
||||||
|
'狻', '夊', '孙', '唆', '他', '囼',
|
||||||
|
'坍', '汤', '夲', '忑', '熥', '剔',
|
||||||
|
'天', '旫', '帖', '厅', '囲', '偷',
|
||||||
|
'凸', '湍', '推', '吞', '乇', '穵',
|
||||||
|
'歪', '弯', '尣', '危', '昷', '翁',
|
||||||
|
'挝', '乌', '夕', '虲', '仚', '乡',
|
||||||
|
'灱', '些', '心', '星', '凶', '休',
|
||||||
|
'吁', '吅', '削', '坃', '丫', '恹',
|
||||||
|
'央', '幺', '倻', '一', '囙', '应',
|
||||||
|
'哟', '佣', '优', '扜', '囦', '曰',
|
||||||
|
'晕', '筠', '筼', '帀', '災', '兂',
|
||||||
|
'匨', '傮', '则', '贼', '怎', '増',
|
||||||
|
'扎', '捚', '沾', '张', '长', '長',
|
||||||
|
'佋', '蜇', '贞', '争', '之', '峙',
|
||||||
|
'庢', '中', '州', '朱', '抓', '拽',
|
||||||
|
'专', '妆', '隹', '宒', '卓', '乲',
|
||||||
|
'宗', '邹', '租', '钻', '厜', '尊',
|
||||||
|
'昨', '兙', '鿃', '鿄'
|
||||||
|
)
|
||||||
|
|
||||||
|
val PINYINS = arrayOf(
|
||||||
|
byteArrayOf(65, 0, 0, 0, 0, 0), byteArrayOf(65, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(65, 78, 0, 0, 0, 0), byteArrayOf(65, 78, 71, 0, 0, 0),
|
||||||
|
byteArrayOf(65, 79, 0, 0, 0, 0), byteArrayOf(66, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 65, 73, 0, 0, 0), byteArrayOf(66, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 65, 78, 71, 0, 0), byteArrayOf(66, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 69, 73, 0, 0, 0), byteArrayOf(66, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 69, 78, 71, 0, 0), byteArrayOf(66, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 73, 65, 78, 0, 0), byteArrayOf(66, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(66, 73, 69, 0, 0, 0), byteArrayOf(66, 73, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 73, 78, 71, 0, 0), byteArrayOf(66, 79, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(66, 85, 0, 0, 0, 0), byteArrayOf(67, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 65, 73, 0, 0, 0), byteArrayOf(67, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 65, 78, 71, 0, 0), byteArrayOf(67, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 69, 0, 0, 0, 0), byteArrayOf(67, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 69, 78, 71, 0, 0), byteArrayOf(90, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(67, 69, 78, 71, 0, 0), byteArrayOf(67, 72, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 65, 73, 0, 0), byteArrayOf(67, 72, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 65, 78, 71, 0), byteArrayOf(67, 72, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 69, 0, 0, 0), byteArrayOf(67, 72, 69, 78, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(67, 72, 69, 78, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 69, 78, 71, 0), byteArrayOf(67, 72, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 79, 78, 71, 0), byteArrayOf(67, 72, 79, 85, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 85, 0, 0, 0), byteArrayOf(67, 72, 85, 65, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 85, 65, 73, 0), byteArrayOf(67, 72, 85, 65, 78, 0),
|
||||||
|
byteArrayOf(67, 72, 85, 65, 78, 71), byteArrayOf(67, 72, 85, 73, 0, 0),
|
||||||
|
byteArrayOf(67, 72, 85, 78, 0, 0), byteArrayOf(67, 72, 85, 79, 0, 0),
|
||||||
|
byteArrayOf(67, 73, 0, 0, 0, 0), byteArrayOf(67, 79, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(67, 79, 85, 0, 0, 0), byteArrayOf(67, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 85, 65, 78, 0, 0), byteArrayOf(67, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(67, 85, 78, 0, 0, 0), byteArrayOf(67, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 65, 0, 0, 0, 0), byteArrayOf(68, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 65, 78, 0, 0, 0), byteArrayOf(68, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(68, 65, 79, 0, 0, 0), byteArrayOf(68, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 69, 78, 0, 0, 0), byteArrayOf(68, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(68, 73, 0, 0, 0, 0), byteArrayOf(68, 73, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 73, 65, 78, 0, 0), byteArrayOf(68, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(68, 73, 69, 0, 0, 0), byteArrayOf(68, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(68, 73, 85, 0, 0, 0), byteArrayOf(68, 79, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(68, 79, 85, 0, 0, 0), byteArrayOf(68, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 85, 65, 78, 0, 0), byteArrayOf(68, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(68, 85, 78, 0, 0, 0), byteArrayOf(68, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(69, 0, 0, 0, 0, 0), byteArrayOf(69, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(69, 78, 0, 0, 0, 0), byteArrayOf(69, 78, 71, 0, 0, 0),
|
||||||
|
byteArrayOf(69, 82, 0, 0, 0, 0), byteArrayOf(70, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(70, 65, 78, 0, 0, 0), byteArrayOf(70, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(70, 69, 73, 0, 0, 0), byteArrayOf(70, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(70, 69, 78, 71, 0, 0), byteArrayOf(70, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(70, 79, 0, 0, 0, 0), byteArrayOf(70, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(70, 85, 0, 0, 0, 0), byteArrayOf(71, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 65, 73, 0, 0, 0), byteArrayOf(71, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 65, 78, 71, 0, 0), byteArrayOf(71, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 69, 0, 0, 0, 0), byteArrayOf(71, 69, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 69, 78, 0, 0, 0), byteArrayOf(71, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(71, 79, 78, 71, 0, 0), byteArrayOf(71, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 85, 0, 0, 0, 0), byteArrayOf(71, 85, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 85, 65, 73, 0, 0), byteArrayOf(71, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(71, 85, 65, 78, 71, 0), byteArrayOf(71, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(71, 85, 78, 0, 0, 0), byteArrayOf(71, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 65, 0, 0, 0, 0), byteArrayOf(72, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 65, 78, 0, 0, 0), byteArrayOf(72, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(72, 65, 79, 0, 0, 0), byteArrayOf(72, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 69, 73, 0, 0, 0), byteArrayOf(72, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 69, 78, 71, 0, 0), byteArrayOf(72, 77, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 79, 78, 71, 0, 0), byteArrayOf(72, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 85, 0, 0, 0, 0), byteArrayOf(72, 85, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 85, 65, 73, 0, 0), byteArrayOf(72, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(72, 85, 65, 78, 71, 0), byteArrayOf(72, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(72, 85, 78, 0, 0, 0), byteArrayOf(72, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(74, 73, 0, 0, 0, 0), byteArrayOf(74, 73, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(74, 73, 65, 78, 0, 0), byteArrayOf(74, 73, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(74, 73, 65, 79, 0, 0), byteArrayOf(74, 73, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(74, 73, 78, 0, 0, 0), byteArrayOf(74, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(74, 73, 79, 78, 71, 0), byteArrayOf(74, 73, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(74, 85, 0, 0, 0, 0), byteArrayOf(74, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(74, 85, 69, 0, 0, 0), byteArrayOf(74, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 65, 0, 0, 0, 0), byteArrayOf(75, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 65, 78, 0, 0, 0), byteArrayOf(75, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(75, 65, 79, 0, 0, 0), byteArrayOf(75, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 69, 78, 0, 0, 0), byteArrayOf(75, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(75, 79, 78, 71, 0, 0), byteArrayOf(75, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 85, 0, 0, 0, 0), byteArrayOf(75, 85, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 85, 65, 73, 0, 0), byteArrayOf(75, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(75, 85, 65, 78, 71, 0), byteArrayOf(75, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(75, 85, 78, 0, 0, 0), byteArrayOf(75, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 65, 0, 0, 0, 0), byteArrayOf(76, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 65, 78, 0, 0, 0), byteArrayOf(76, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(76, 65, 79, 0, 0, 0), byteArrayOf(76, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 69, 73, 0, 0, 0), byteArrayOf(76, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(76, 73, 0, 0, 0, 0), byteArrayOf(76, 73, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 73, 65, 78, 0, 0), byteArrayOf(76, 73, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(76, 73, 65, 79, 0, 0), byteArrayOf(76, 73, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 73, 78, 0, 0, 0), byteArrayOf(76, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(76, 73, 85, 0, 0, 0), byteArrayOf(76, 79, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 79, 78, 71, 0, 0), byteArrayOf(76, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 85, 0, 0, 0, 0), byteArrayOf(76, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(76, 85, 69, 0, 0, 0), byteArrayOf(76, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(76, 85, 79, 0, 0, 0), byteArrayOf(77, 0, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 65, 0, 0, 0, 0), byteArrayOf(77, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 65, 78, 0, 0, 0), byteArrayOf(77, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(77, 65, 79, 0, 0, 0), byteArrayOf(77, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 69, 73, 0, 0, 0), byteArrayOf(77, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 69, 78, 71, 0, 0), byteArrayOf(77, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 73, 65, 78, 0, 0), byteArrayOf(77, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(77, 73, 69, 0, 0, 0), byteArrayOf(77, 73, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 73, 78, 71, 0, 0), byteArrayOf(77, 73, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 79, 0, 0, 0, 0), byteArrayOf(77, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(77, 85, 0, 0, 0, 0), byteArrayOf(78, 0, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 65, 0, 0, 0, 0), byteArrayOf(78, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 65, 78, 0, 0, 0), byteArrayOf(78, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(78, 65, 79, 0, 0, 0), byteArrayOf(78, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 69, 73, 0, 0, 0), byteArrayOf(78, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 69, 78, 71, 0, 0), byteArrayOf(78, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 73, 65, 78, 0, 0), byteArrayOf(78, 73, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(78, 73, 65, 79, 0, 0), byteArrayOf(78, 73, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 73, 78, 0, 0, 0), byteArrayOf(78, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(78, 73, 85, 0, 0, 0), byteArrayOf(78, 79, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(78, 79, 85, 0, 0, 0), byteArrayOf(78, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 85, 65, 78, 0, 0), byteArrayOf(78, 85, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(78, 85, 78, 0, 0, 0), byteArrayOf(78, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(79, 0, 0, 0, 0, 0), byteArrayOf(79, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(80, 65, 0, 0, 0, 0), byteArrayOf(80, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(80, 65, 78, 0, 0, 0), byteArrayOf(80, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(80, 65, 79, 0, 0, 0), byteArrayOf(80, 69, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(80, 69, 78, 0, 0, 0), byteArrayOf(80, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(80, 73, 0, 0, 0, 0), byteArrayOf(80, 73, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(80, 73, 65, 79, 0, 0), byteArrayOf(80, 73, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(80, 73, 78, 0, 0, 0), byteArrayOf(80, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(80, 79, 0, 0, 0, 0), byteArrayOf(80, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(80, 85, 0, 0, 0, 0), byteArrayOf(81, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(81, 73, 65, 0, 0, 0), byteArrayOf(81, 73, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(81, 73, 65, 78, 71, 0), byteArrayOf(81, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(81, 73, 69, 0, 0, 0), byteArrayOf(81, 73, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(81, 73, 78, 71, 0, 0), byteArrayOf(81, 73, 79, 78, 71, 0),
|
||||||
|
byteArrayOf(81, 73, 85, 0, 0, 0), byteArrayOf(81, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(81, 85, 65, 78, 0, 0), byteArrayOf(81, 85, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(81, 85, 78, 0, 0, 0), byteArrayOf(82, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 65, 78, 71, 0, 0), byteArrayOf(82, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 69, 0, 0, 0, 0), byteArrayOf(82, 69, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 69, 78, 71, 0, 0), byteArrayOf(82, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 79, 78, 71, 0, 0), byteArrayOf(82, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 85, 0, 0, 0, 0), byteArrayOf(82, 85, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 85, 65, 78, 0, 0), byteArrayOf(82, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(82, 85, 78, 0, 0, 0), byteArrayOf(82, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 65, 0, 0, 0, 0), byteArrayOf(83, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 65, 78, 0, 0, 0), byteArrayOf(83, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(83, 65, 79, 0, 0, 0), byteArrayOf(83, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 69, 78, 0, 0, 0), byteArrayOf(83, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 65, 0, 0, 0), byteArrayOf(83, 72, 65, 73, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 65, 78, 0, 0), byteArrayOf(83, 72, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(83, 72, 65, 79, 0, 0), byteArrayOf(83, 72, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(88, 73, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 69, 78, 0, 0), byteArrayOf(83, 72, 69, 78, 71, 0),
|
||||||
|
byteArrayOf(83, 72, 73, 0, 0, 0), byteArrayOf(83, 72, 79, 85, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 85, 0, 0, 0), byteArrayOf(83, 72, 85, 65, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 85, 65, 73, 0), byteArrayOf(83, 72, 85, 65, 78, 0),
|
||||||
|
byteArrayOf(83, 72, 85, 65, 78, 71), byteArrayOf(83, 72, 85, 73, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 85, 78, 0, 0), byteArrayOf(83, 72, 85, 79, 0, 0),
|
||||||
|
byteArrayOf(83, 73, 0, 0, 0, 0), byteArrayOf(83, 79, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(83, 79, 85, 0, 0, 0), byteArrayOf(83, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 85, 65, 78, 0, 0), byteArrayOf(83, 85, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 85, 78, 0, 0, 0), byteArrayOf(83, 85, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 65, 0, 0, 0, 0), byteArrayOf(84, 65, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 65, 78, 0, 0, 0), byteArrayOf(84, 65, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(84, 65, 79, 0, 0, 0), byteArrayOf(84, 69, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 69, 78, 71, 0, 0), byteArrayOf(84, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 73, 65, 78, 0, 0), byteArrayOf(84, 73, 65, 79, 0, 0),
|
||||||
|
byteArrayOf(84, 73, 69, 0, 0, 0), byteArrayOf(84, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(84, 79, 78, 71, 0, 0), byteArrayOf(84, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 85, 0, 0, 0, 0), byteArrayOf(84, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(84, 85, 73, 0, 0, 0), byteArrayOf(84, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(84, 85, 79, 0, 0, 0), byteArrayOf(87, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(87, 65, 73, 0, 0, 0), byteArrayOf(87, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(87, 65, 78, 71, 0, 0), byteArrayOf(87, 69, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(87, 69, 78, 0, 0, 0), byteArrayOf(87, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(87, 79, 0, 0, 0, 0), byteArrayOf(87, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(88, 73, 0, 0, 0, 0), byteArrayOf(88, 73, 65, 0, 0, 0),
|
||||||
|
byteArrayOf(88, 73, 65, 78, 0, 0), byteArrayOf(88, 73, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(88, 73, 65, 79, 0, 0), byteArrayOf(88, 73, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(88, 73, 78, 0, 0, 0), byteArrayOf(88, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(88, 73, 79, 78, 71, 0), byteArrayOf(88, 73, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(88, 85, 0, 0, 0, 0), byteArrayOf(88, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(88, 85, 69, 0, 0, 0), byteArrayOf(88, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 65, 0, 0, 0, 0), byteArrayOf(89, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 65, 78, 71, 0, 0), byteArrayOf(89, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 69, 0, 0, 0, 0), byteArrayOf(89, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 73, 78, 0, 0, 0), byteArrayOf(89, 73, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(89, 79, 0, 0, 0, 0), byteArrayOf(89, 79, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(89, 79, 85, 0, 0, 0), byteArrayOf(89, 85, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 85, 65, 78, 0, 0), byteArrayOf(89, 85, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 85, 78, 0, 0, 0), byteArrayOf(74, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(89, 85, 78, 0, 0, 0), byteArrayOf(90, 65, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 65, 73, 0, 0, 0), byteArrayOf(90, 65, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 65, 78, 71, 0, 0), byteArrayOf(90, 65, 79, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 69, 0, 0, 0, 0), byteArrayOf(90, 69, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 69, 78, 0, 0, 0), byteArrayOf(90, 69, 78, 71, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 65, 0, 0, 0), byteArrayOf(90, 72, 65, 73, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 65, 78, 0, 0), byteArrayOf(90, 72, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(67, 72, 65, 78, 71, 0), byteArrayOf(90, 72, 65, 78, 71, 0),
|
||||||
|
byteArrayOf(90, 72, 65, 79, 0, 0), byteArrayOf(90, 72, 69, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 69, 78, 0, 0), byteArrayOf(90, 72, 69, 78, 71, 0),
|
||||||
|
byteArrayOf(90, 72, 73, 0, 0, 0), byteArrayOf(83, 72, 73, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 73, 0, 0, 0), byteArrayOf(90, 72, 79, 78, 71, 0),
|
||||||
|
byteArrayOf(90, 72, 79, 85, 0, 0), byteArrayOf(90, 72, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 85, 65, 0, 0), byteArrayOf(90, 72, 85, 65, 73, 0),
|
||||||
|
byteArrayOf(90, 72, 85, 65, 78, 0), byteArrayOf(90, 72, 85, 65, 78, 71),
|
||||||
|
byteArrayOf(90, 72, 85, 73, 0, 0), byteArrayOf(90, 72, 85, 78, 0, 0),
|
||||||
|
byteArrayOf(90, 72, 85, 79, 0, 0), byteArrayOf(90, 73, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 79, 78, 71, 0, 0), byteArrayOf(90, 79, 85, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 85, 0, 0, 0, 0), byteArrayOf(90, 85, 65, 78, 0, 0),
|
||||||
|
byteArrayOf(90, 85, 73, 0, 0, 0), byteArrayOf(90, 85, 78, 0, 0, 0),
|
||||||
|
byteArrayOf(90, 85, 79, 0, 0, 0), byteArrayOf(0, 0, 0, 0, 0, 0),
|
||||||
|
byteArrayOf(83, 72, 65, 78, 0, 0), byteArrayOf(0, 0, 0, 0, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val FIRST_PINYIN_UNIHAN = "阿"
|
||||||
|
private const val LAST_PINYIN_UNIHAN = "鿿"
|
||||||
|
|
||||||
|
private val COLLATOR: Collator = Collator.getInstance(Locale.CHINA)
|
||||||
|
|
||||||
|
private var sInstance: HanziToPinyin? = null
|
||||||
|
|
||||||
|
fun getInstance(): HanziToPinyin {
|
||||||
|
synchronized(HanziToPinyin::class.java) {
|
||||||
|
if (sInstance != null) {
|
||||||
|
return sInstance!!
|
||||||
|
}
|
||||||
|
|
||||||
|
val locale = Collator.getAvailableLocales()
|
||||||
|
for (value in locale) {
|
||||||
|
if (value == Locale.CHINA || value.language.contains("zh")) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Self validation. Result: ${doSelfValidation()}")
|
||||||
|
}
|
||||||
|
sInstance = HanziToPinyin(true)
|
||||||
|
return sInstance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sInstance == null) {
|
||||||
|
if (Locale.CHINA == Locale.getDefault()) {
|
||||||
|
sInstance = HanziToPinyin(true)
|
||||||
|
return sInstance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.w(TAG, "There is no Chinese collator, HanziToPinyin is disabled")
|
||||||
|
sInstance = HanziToPinyin(false)
|
||||||
|
return sInstance!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doSelfValidation(): Boolean {
|
||||||
|
val lastChar = UNIHANS[0]
|
||||||
|
var lastString = lastChar.toString()
|
||||||
|
for (c in UNIHANS) {
|
||||||
|
if (lastChar == c) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val curString = c.toString()
|
||||||
|
val cmp = COLLATOR.compare(lastString, curString)
|
||||||
|
if (cmp >= 0) {
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"Internal error in Unihan table. The last string \"$lastString\" " +
|
||||||
|
"is greater than current string \"$curString\"."
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
lastString = curString
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -105,7 +105,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||||
modules.filter {
|
modules.filter {
|
||||||
it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance()
|
it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance()
|
||||||
.toPinyinString(it.name).contains(search, true)
|
.toPinyinString(it.name)?.contains(search, true) == true
|
||||||
}.sortedWith(comparator).also {
|
}.sortedWith(comparator).also {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class SuperUserViewModel : ViewModel() {
|
|||||||
group.apps.any { app ->
|
group.apps.any { app ->
|
||||||
app.label.contains(search, true) ||
|
app.label.contains(search, true) ||
|
||||||
app.packageName.contains(search, true) ||
|
app.packageName.contains(search, true) ||
|
||||||
HanziToPinyin.getInstance().toPinyinString(app.label).contains(search, true)
|
HanziToPinyin.getInstance().toPinyinString(app.label)?.contains(search, true) == true
|
||||||
}
|
}
|
||||||
}.filter { group ->
|
}.filter { group ->
|
||||||
group.uid == 2000 || showSystemApps ||
|
group.uid == 2000 || showSystemApps ||
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package com.sukisu.ultra.ui.webui;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.util.LruCache;
|
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel;
|
|
||||||
|
|
||||||
public class AppIconUtil {
|
|
||||||
// Limit cache size to 200 icons
|
|
||||||
private static final int CACHE_SIZE = 200;
|
|
||||||
private static final LruCache<String, Bitmap> iconCache = new LruCache<>(CACHE_SIZE);
|
|
||||||
|
|
||||||
public static synchronized Bitmap loadAppIconSync(Context context, String packageName, int sizePx) {
|
|
||||||
Bitmap cached = iconCache.get(packageName);
|
|
||||||
if (cached != null) return cached;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Drawable drawable = SuperUserViewModel.getAppIconDrawable(context, packageName);
|
|
||||||
if (drawable == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Bitmap raw = drawableToBitmap(drawable, sizePx);
|
|
||||||
Bitmap icon = Bitmap.createScaledBitmap(raw, sizePx, sizePx, true);
|
|
||||||
if (raw != icon) raw.recycle();
|
|
||||||
iconCache.put(packageName, icon);
|
|
||||||
return icon;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Bitmap drawableToBitmap(Drawable drawable, int size) {
|
|
||||||
if (drawable instanceof BitmapDrawable) return ((BitmapDrawable) drawable).getBitmap();
|
|
||||||
|
|
||||||
int width = drawable.getIntrinsicWidth() > 0 ? drawable.getIntrinsicWidth() : size;
|
|
||||||
int height = drawable.getIntrinsicHeight() > 0 ? drawable.getIntrinsicHeight() : size;
|
|
||||||
|
|
||||||
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bmp);
|
|
||||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
|
||||||
drawable.draw(canvas);
|
|
||||||
return bmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.sukisu.ultra.ui.webui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.LruCache
|
||||||
|
import androidx.core.graphics.createBitmap
|
||||||
|
import androidx.core.graphics.scale
|
||||||
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel.Companion.getAppIconDrawable
|
||||||
|
|
||||||
|
object AppIconUtil {
|
||||||
|
// Limit cache size to 200 icons
|
||||||
|
private const val CACHE_SIZE = 200
|
||||||
|
private val iconCache = LruCache<String?, Bitmap?>(CACHE_SIZE)
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun loadAppIconSync(context: Context, packageName: String, sizePx: Int): Bitmap? {
|
||||||
|
val cached = iconCache.get(packageName)
|
||||||
|
if (cached != null) return cached
|
||||||
|
|
||||||
|
try {
|
||||||
|
val drawable = getAppIconDrawable(context, packageName) ?: return null
|
||||||
|
val raw = drawableToBitmap(drawable, sizePx)
|
||||||
|
val icon = raw.scale(sizePx, sizePx)
|
||||||
|
if (raw != icon) raw.recycle()
|
||||||
|
iconCache.put(packageName, icon)
|
||||||
|
return icon
|
||||||
|
} catch (_: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawableToBitmap(drawable: Drawable, size: Int): Bitmap {
|
||||||
|
if (drawable is BitmapDrawable) return drawable.bitmap
|
||||||
|
|
||||||
|
val width = if (drawable.intrinsicWidth > 0) drawable.intrinsicWidth else size
|
||||||
|
val height = if (drawable.intrinsicHeight > 0) drawable.intrinsicHeight else size
|
||||||
|
|
||||||
|
val bmp = createBitmap(width, height)
|
||||||
|
val canvas = Canvas(bmp)
|
||||||
|
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||||
|
drawable.draw(canvas)
|
||||||
|
return bmp
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2023 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.sukisu.ultra.ui.webui;
|
|
||||||
|
|
||||||
import java.net.URLConnection;
|
|
||||||
|
|
||||||
class MimeUtil {
|
|
||||||
|
|
||||||
public static String getMimeFromFileName(String fileName) {
|
|
||||||
if (fileName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copying the logic and mapping that Chromium follows.
|
|
||||||
// First we check against the OS (this is a limited list by default)
|
|
||||||
// but app developers can extend this.
|
|
||||||
// We then check against a list of hardcoded mime types above if the
|
|
||||||
// OS didn't provide a result.
|
|
||||||
String mimeType = URLConnection.guessContentTypeFromName(fileName);
|
|
||||||
|
|
||||||
if (mimeType != null) {
|
|
||||||
return mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return guessHardcodedMime(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should keep this map in sync with the lists under
|
|
||||||
// //net/base/mime_util.cc in Chromium.
|
|
||||||
// A bunch of the mime types don't really apply to Android land
|
|
||||||
// like word docs so feel free to filter out where necessary.
|
|
||||||
private static String guessHardcodedMime(String fileName) {
|
|
||||||
int finalFullStop = fileName.lastIndexOf('.');
|
|
||||||
if (finalFullStop == -1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String extension = fileName.substring(finalFullStop + 1).toLowerCase();
|
|
||||||
|
|
||||||
return switch (extension) {
|
|
||||||
case "webm" -> "video/webm";
|
|
||||||
case "mpeg", "mpg" -> "video/mpeg";
|
|
||||||
case "mp3" -> "audio/mpeg";
|
|
||||||
case "wasm" -> "application/wasm";
|
|
||||||
case "xhtml", "xht", "xhtm" -> "application/xhtml+xml";
|
|
||||||
case "flac" -> "audio/flac";
|
|
||||||
case "ogg", "oga", "opus" -> "audio/ogg";
|
|
||||||
case "wav" -> "audio/wav";
|
|
||||||
case "m4a" -> "audio/x-m4a";
|
|
||||||
case "gif" -> "image/gif";
|
|
||||||
case "jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg";
|
|
||||||
case "png" -> "image/png";
|
|
||||||
case "apng" -> "image/apng";
|
|
||||||
case "svg", "svgz" -> "image/svg+xml";
|
|
||||||
case "webp" -> "image/webp";
|
|
||||||
case "mht", "mhtml" -> "multipart/related";
|
|
||||||
case "css" -> "text/css";
|
|
||||||
case "html", "htm", "shtml", "shtm", "ehtml" -> "text/html";
|
|
||||||
case "js", "mjs" -> "application/javascript";
|
|
||||||
case "xml" -> "text/xml";
|
|
||||||
case "mp4", "m4v" -> "video/mp4";
|
|
||||||
case "ogv", "ogm" -> "video/ogg";
|
|
||||||
case "ico" -> "image/x-icon";
|
|
||||||
case "woff" -> "application/font-woff";
|
|
||||||
case "gz", "tgz" -> "application/gzip";
|
|
||||||
case "json" -> "application/json";
|
|
||||||
case "pdf" -> "application/pdf";
|
|
||||||
case "zip" -> "application/zip";
|
|
||||||
case "bmp" -> "image/bmp";
|
|
||||||
case "tiff", "tif" -> "image/tiff";
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sukisu.ultra.ui.webui
|
||||||
|
|
||||||
|
import java.net.URLConnection
|
||||||
|
|
||||||
|
internal object MimeUtil {
|
||||||
|
fun getMimeFromFileName(fileName: String?): String? {
|
||||||
|
if (fileName == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val mimeType = URLConnection.guessContentTypeFromName(fileName)
|
||||||
|
if (mimeType != null) {
|
||||||
|
return mimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
return guessHardcodedMime(fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun guessHardcodedMime(fileName: String): String? {
|
||||||
|
val finalFullStop = fileName.lastIndexOf('.')
|
||||||
|
if (finalFullStop == -1) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val extension = fileName.substring(finalFullStop + 1).lowercase()
|
||||||
|
|
||||||
|
return when (extension) {
|
||||||
|
"webm" -> "video/webm"
|
||||||
|
"mpeg", "mpg" -> "video/mpeg"
|
||||||
|
"mp3" -> "audio/mpeg"
|
||||||
|
"wasm" -> "application/wasm"
|
||||||
|
"xhtml", "xht", "xhtm" -> "application/xhtml+xml"
|
||||||
|
"flac" -> "audio/flac"
|
||||||
|
"ogg", "oga", "opus" -> "audio/ogg"
|
||||||
|
"wav" -> "audio/wav"
|
||||||
|
"m4a" -> "audio/x-m4a"
|
||||||
|
"gif" -> "image/gif"
|
||||||
|
"jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg"
|
||||||
|
"png" -> "image/png"
|
||||||
|
"apng" -> "image/apng"
|
||||||
|
"svg", "svgz" -> "image/svg+xml"
|
||||||
|
"webp" -> "image/webp"
|
||||||
|
"mht", "mhtml" -> "multipart/related"
|
||||||
|
"css" -> "text/css"
|
||||||
|
"html", "htm", "shtml", "shtm", "ehtml" -> "text/html"
|
||||||
|
"js", "mjs" -> "application/javascript"
|
||||||
|
"xml" -> "text/xml"
|
||||||
|
"mp4", "m4v" -> "video/mp4"
|
||||||
|
"ogv", "ogm" -> "video/ogg"
|
||||||
|
"ico" -> "image/x-icon"
|
||||||
|
"woff" -> "application/font-woff"
|
||||||
|
"gz", "tgz" -> "application/gzip"
|
||||||
|
"json" -> "application/json"
|
||||||
|
"pdf" -> "application/pdf"
|
||||||
|
"zip" -> "application/zip"
|
||||||
|
"bmp" -> "image/bmp"
|
||||||
|
"tiff", "tif" -> "image/tiff"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
package com.sukisu.ultra.ui.webui;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.webkit.WebResourceResponse;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.WorkerThread;
|
|
||||||
import androidx.webkit.WebViewAssetLoader;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler class to open files from file system by root access
|
|
||||||
* For more information about android storage please refer to
|
|
||||||
* <a href="https://developer.android.com/guide/topics/data/data-storage">Android Developers
|
|
||||||
* Docs: Data and file storage overview</a>.
|
|
||||||
* <p class="note">
|
|
||||||
* To avoid leaking user or app data to the web, make sure to choose {@code directory}
|
|
||||||
* carefully, and assume any file under this directory could be accessed by any web page subject
|
|
||||||
* to same-origin rules.
|
|
||||||
* <p>
|
|
||||||
* A typical usage would be like:
|
|
||||||
* <pre class="prettyprint">
|
|
||||||
* File publicDir = new File(context.getFilesDir(), "public");
|
|
||||||
* // Host "files/public/" in app's data directory under:
|
|
||||||
* // http://appassets.androidplatform.net/public/...
|
|
||||||
* WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
|
|
||||||
* .addPathHandler("/public/", new InternalStoragePathHandler(context, publicDir))
|
|
||||||
* .build();
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler {
|
|
||||||
private static final String TAG = "SuFilePathHandler";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default value to be used as MIME type if guessing MIME type failed.
|
|
||||||
*/
|
|
||||||
public static final String DEFAULT_MIME_TYPE = "text/plain";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forbidden subdirectories of {@link Context#getDataDir} that cannot be exposed by this
|
|
||||||
* handler. They are forbidden as they often contain sensitive information.
|
|
||||||
* <p class="note">
|
|
||||||
* Note: Any future addition to this list will be considered breaking changes to the API.
|
|
||||||
*/
|
|
||||||
private static final String[] FORBIDDEN_DATA_DIRS =
|
|
||||||
new String[] {"/data/data", "/data/system"};
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final File mDirectory;
|
|
||||||
|
|
||||||
private final Shell mShell;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates PathHandler for app's internal storage.
|
|
||||||
* The directory to be exposed must be inside either the application's internal data
|
|
||||||
* directory {@link Context#getDataDir} or cache directory {@link Context#getCacheDir}.
|
|
||||||
* External storage is not supported for security reasons, as other apps with
|
|
||||||
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} may be able to modify the
|
|
||||||
* files.
|
|
||||||
* <p>
|
|
||||||
* Exposing the entire data or cache directory is not permitted, to avoid accidentally
|
|
||||||
* exposing sensitive application files to the web. Certain existing subdirectories of
|
|
||||||
* {@link Context#getDataDir} are also not permitted as they are often sensitive.
|
|
||||||
* These files are ({@code "app_webview/"}, {@code "databases/"}, {@code "lib/"},
|
|
||||||
* {@code "shared_prefs/"} and {@code "code_cache/"}).
|
|
||||||
* <p>
|
|
||||||
* The application should typically use a dedicated subdirectory for the files it intends to
|
|
||||||
* expose and keep them separate from other files.
|
|
||||||
*
|
|
||||||
* @param directory the absolute path of the exposed app internal storage directory from
|
|
||||||
* which files can be loaded.
|
|
||||||
* @throws IllegalArgumentException if the directory is not allowed.
|
|
||||||
*/
|
|
||||||
public SuFilePathHandler(@NonNull File directory, Shell rootShell) {
|
|
||||||
try {
|
|
||||||
mDirectory = new File(getCanonicalDirPath(directory));
|
|
||||||
if (!isAllowedInternalStorageDir()) {
|
|
||||||
throw new IllegalArgumentException("The given directory \"" + directory
|
|
||||||
+ "\" doesn't exist under an allowed app internal storage directory");
|
|
||||||
}
|
|
||||||
mShell = rootShell;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Failed to resolve the canonical path for the given directory: "
|
|
||||||
+ directory.getPath(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAllowedInternalStorageDir() throws IOException {
|
|
||||||
String dir = getCanonicalDirPath(mDirectory);
|
|
||||||
|
|
||||||
for (String forbiddenPath : FORBIDDEN_DATA_DIRS) {
|
|
||||||
if (dir.startsWith(forbiddenPath)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the requested file from the exposed data directory.
|
|
||||||
* <p>
|
|
||||||
* The matched prefix path used shouldn't be a prefix of a real web path. Thus, if the
|
|
||||||
* requested file cannot be found or is outside the mounted directory a
|
|
||||||
* {@link WebResourceResponse} object with a {@code null} {@link InputStream} will be
|
|
||||||
* returned instead of {@code null}. This saves the time of falling back to network and
|
|
||||||
* trying to resolve a path that doesn't exist. A {@link WebResourceResponse} with
|
|
||||||
* {@code null} {@link InputStream} will be received as an HTTP response with status code
|
|
||||||
* {@code 404} and no body.
|
|
||||||
* <p class="note">
|
|
||||||
* The MIME type for the file will be determined from the file's extension using
|
|
||||||
* {@link java.net.URLConnection#guessContentTypeFromName}. Developers should ensure that
|
|
||||||
* files are named using standard file extensions. If the file does not have a
|
|
||||||
* recognised extension, {@code "text/plain"} will be used by default.
|
|
||||||
*
|
|
||||||
* @param path the suffix path to be handled.
|
|
||||||
* @return {@link WebResourceResponse} for the requested file.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@WorkerThread
|
|
||||||
@NonNull
|
|
||||||
public WebResourceResponse handle(@NonNull String path) {
|
|
||||||
try {
|
|
||||||
File file = getCanonicalFileIfChild(mDirectory, path);
|
|
||||||
if (file != null) {
|
|
||||||
InputStream is = openFile(file, mShell);
|
|
||||||
String mimeType = guessMimeType(path);
|
|
||||||
return new WebResourceResponse(mimeType, null, is);
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, String.format(
|
|
||||||
"The requested file: %s is outside the mounted directory: %s", path,
|
|
||||||
mDirectory));
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Error opening the requested path: " + path, e);
|
|
||||||
}
|
|
||||||
return new WebResourceResponse(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCanonicalDirPath(@NonNull File file) throws IOException {
|
|
||||||
String canonicalPath = file.getCanonicalPath();
|
|
||||||
if (!canonicalPath.endsWith("/")) canonicalPath += "/";
|
|
||||||
return canonicalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getCanonicalFileIfChild(@NonNull File parent, @NonNull String child)
|
|
||||||
throws IOException {
|
|
||||||
String parentCanonicalPath = getCanonicalDirPath(parent);
|
|
||||||
String childCanonicalPath = new File(parent, child).getCanonicalPath();
|
|
||||||
if (childCanonicalPath.startsWith(parentCanonicalPath)) {
|
|
||||||
return new File(childCanonicalPath);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private static InputStream handleSvgzStream(@NonNull String path,
|
|
||||||
@NonNull InputStream stream) throws IOException {
|
|
||||||
return path.endsWith(".svgz") ? new GZIPInputStream(stream) : stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InputStream openFile(@NonNull File file, @NonNull Shell shell) throws IOException {
|
|
||||||
SuFile suFile = new SuFile(file.getAbsolutePath());
|
|
||||||
suFile.setShell(shell);
|
|
||||||
InputStream fis = SuFileInputStream.open(suFile);
|
|
||||||
return handleSvgzStream(file.getPath(), fis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use {@link MimeUtil#getMimeFromFileName} to guess MIME type or return the
|
|
||||||
* {@link #DEFAULT_MIME_TYPE} if it can't guess.
|
|
||||||
*
|
|
||||||
* @param filePath path of the file to guess its MIME type.
|
|
||||||
* @return MIME type guessed from file extension or {@link #DEFAULT_MIME_TYPE}.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static String guessMimeType(@NonNull String filePath) {
|
|
||||||
String mimeType = MimeUtil.getMimeFromFileName(filePath);
|
|
||||||
return mimeType == null ? DEFAULT_MIME_TYPE : mimeType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.sukisu.ultra.ui.webui
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.webkit.WebResourceResponse
|
||||||
|
import androidx.annotation.WorkerThread
|
||||||
|
import androidx.webkit.WebViewAssetLoader
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
|
||||||
|
class SuFilePathHandler(
|
||||||
|
directory: File,
|
||||||
|
private val mShell: Shell
|
||||||
|
) : WebViewAssetLoader.PathHandler {
|
||||||
|
|
||||||
|
private val mDirectory: File
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
mDirectory = File(getCanonicalDirPath(directory))
|
||||||
|
if (!isAllowedInternalStorageDir()) {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"The given directory \"$directory\" doesn't exist under an allowed app internal storage directory"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"Failed to resolve the canonical path for the given directory: ${directory.path}",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isAllowedInternalStorageDir(): Boolean {
|
||||||
|
return try {
|
||||||
|
val dir = getCanonicalDirPath(mDirectory)
|
||||||
|
FORBIDDEN_DATA_DIRS.none { dir.startsWith(it) }
|
||||||
|
} catch (_: IOException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
override fun handle(path: String): WebResourceResponse {
|
||||||
|
try {
|
||||||
|
val file = getCanonicalFileIfChild(mDirectory, path)
|
||||||
|
if (file != null) {
|
||||||
|
val inputStream = openFile(file, mShell)
|
||||||
|
val mimeType = guessMimeType(path)
|
||||||
|
return WebResourceResponse(mimeType, null, inputStream)
|
||||||
|
} else {
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"The requested file: $path is outside the mounted directory: $mDirectory"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Error opening the requested path: $path", e)
|
||||||
|
}
|
||||||
|
return WebResourceResponse(null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SuFilePathHandler"
|
||||||
|
const val DEFAULT_MIME_TYPE = "text/plain"
|
||||||
|
|
||||||
|
private val FORBIDDEN_DATA_DIRS = arrayOf("/data/data", "/data/system")
|
||||||
|
|
||||||
|
fun getCanonicalDirPath(file: File): String {
|
||||||
|
val canonicalPath = file.canonicalPath
|
||||||
|
return if (!canonicalPath.endsWith("/")) "$canonicalPath/" else canonicalPath
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCanonicalFileIfChild(parent: File, child: String): File? {
|
||||||
|
return try {
|
||||||
|
val parentCanonicalPath = getCanonicalDirPath(parent)
|
||||||
|
val childCanonicalPath = File(parent, child).canonicalPath
|
||||||
|
if (childCanonicalPath.startsWith(parentCanonicalPath)) {
|
||||||
|
File(childCanonicalPath)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} catch (_: IOException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSvgzStream(path: String, stream: InputStream): InputStream {
|
||||||
|
return if (path.endsWith(".svgz")) GZIPInputStream(stream) else stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openFile(file: File, shell: Shell): InputStream {
|
||||||
|
val suFile = SuFile(file.absolutePath).apply {
|
||||||
|
setShell(shell)
|
||||||
|
}
|
||||||
|
val fis = SuFileInputStream.open(suFile)
|
||||||
|
return handleSvgzStream(file.path, fis)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun guessMimeType(filePath: String): String {
|
||||||
|
return MimeUtil.getMimeFromFileName(filePath) ?: DEFAULT_MIME_TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user