Add tool Base Converter.

This commit is contained in:
2023-01-11 16:35:38 +08:00
parent d5d12fa54c
commit 0e5567a072
19 changed files with 520 additions and 23 deletions

View File

@@ -67,6 +67,16 @@
<intent-filter>
<action android:name="oxygentoolbox.ip" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".tools.converter.MainActivity"
android:exported="false"
android:theme="@style/Theme.OxygenToolbox.Default">
<intent-filter>
<action android:name="oxygentoolbox.converter" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

View File

@@ -36,6 +36,13 @@
"en": "IP Query"
},
"activity": "com.fatapp.oxygentoolbox.tools.ip.MainActivity"
},
{
"text": {
"cn": "\u8fdb\u5236\u8f6c\u6362",
"en": "Base Converter"
},
"activity": "com.fatapp.oxygentoolbox.tools.converter.MainActivity"
}
]
}

View File

@@ -0,0 +1,161 @@
package com.fatapp.oxygentoolbox.tools.converter;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.fatapp.oxygentoolbox.R;
import com.fatapp.oxygentoolbox.util.HexConversionUtils;
import com.fatapp.oxygentoolbox.util.ResourceUtil;
import com.fatapp.oxygentoolbox.util.tool.BaseActivityNormal;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import java.util.regex.Pattern;
public class MainActivity extends BaseActivityNormal {
private final String[] BASE_ITEMS = new String[35];
{
for (int i = 0; i < 35; i++) {
switch (i + 2) {
case 2:
BASE_ITEMS[i] = ResourceUtil.getString(R.string.tool_converter_base_2);
break;
case 8:
BASE_ITEMS[i] = ResourceUtil.getString(R.string.tool_converter_base_8);
break;
case 10:
BASE_ITEMS[i] = ResourceUtil.getString(R.string.tool_converter_base_10);
break;
case 16:
BASE_ITEMS[i] = ResourceUtil.getString(R.string.tool_converter_base_16);
break;
default:
BASE_ITEMS[i] = String.valueOf(i + 2);
}
}
}
private int baseFrom = 10;
private int baseTo = 2;
private TextView textViewBaseFrom;
private ImageView imageViewSwap;
private TextView textViewBaseTo;
private EditText editTextFrom;
private TextView textViewTo;
private ImageView imageViewConvert;
private void initView() {
textViewBaseFrom = findViewById(R.id.text_view_base_from);
imageViewSwap = findViewById(R.id.image_view_swap);
textViewBaseTo = findViewById(R.id.text_view_base_to);
editTextFrom = findViewById(R.id.edit_text_from);
textViewTo = findViewById(R.id.text_view_to);
imageViewConvert = findViewById(R.id.image_view_convert);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadView(this, R.layout.activity_tool_converter);
initView();
initBaseChoose();
initConverter();
}
private void initBaseChoose() {
textViewBaseFrom.setOnClickListener(view -> new MaterialAlertDialogBuilder(this)
.setTitle(ResourceUtil.getString(R.string.tool_converter_choose_base))
.setItems(BASE_ITEMS, (dialogInterface, i) -> {
textViewBaseFrom.setText(BASE_ITEMS[i]);
baseFrom = i + 2;
editTextFrom.setText(null);
textViewTo.setText(null);
})
.show());
textViewBaseTo.setOnClickListener(view -> new MaterialAlertDialogBuilder(this)
.setTitle(ResourceUtil.getString(R.string.tool_converter_choose_base))
.setItems(BASE_ITEMS, (dialogInterface, i) -> {
textViewBaseTo.setText(BASE_ITEMS[i]);
baseTo = i + 2;
if (textViewTo.getText().length() > 0) {
convert();
}
})
.show());
imageViewSwap.setOnClickListener(view -> {
AnimatedVectorDrawable animatedVectorDrawable = (AnimatedVectorDrawable) imageViewSwap.getDrawable();
animatedVectorDrawable.start();
{
int temp = baseFrom;
baseFrom = baseTo;
baseTo = temp;
}
{
String temp = textViewBaseFrom.getText().toString();
textViewBaseFrom.setText(textViewBaseTo.getText());
textViewBaseTo.setText(temp);
}
editTextFrom.setText(null);
textViewTo.setText(null);
});
}
private void initConverter() {
StringBuffer beforeText = new StringBuffer();
editTextFrom.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
beforeText.delete(0, beforeText.length());
beforeText.append(charSequence);
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
imageViewConvert.setEnabled(charSequence.length() != 0);
if (!Pattern.compile(HexConversionUtils.getRegExp(baseFrom)).matcher(charSequence).matches()) {
editTextFrom.setText(beforeText.toString());
editTextFrom.setSelection(beforeText.toString().length() - 1);
} else {
textViewTo.setText(null);
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
textViewTo.setOnFocusChangeListener((view, b) -> ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(editTextFrom.getWindowToken(), 0));
imageViewConvert.setEnabled(false);
imageViewConvert.setOnClickListener(view -> convert());
}
private void convert() {
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(editTextFrom.getWindowToken(), 0);
if (baseFrom == baseTo) {
textViewTo.setText(editTextFrom.getText());
}
try {
String result = HexConversionUtils.decimalToBase(HexConversionUtils.baseToDecimal(editTextFrom.getText().toString(), baseFrom), baseTo);
textViewTo.setText(result);
} catch (Exception e) {
textViewTo.setText(ResourceUtil.getString(R.string.base_nan));
Snackbar.make(getConstraintLayoutRoot(), ResourceUtil.getString(R.string.tool_converter_illegal_number), Snackbar.LENGTH_LONG).show();
}
}
}

View File

@@ -0,0 +1,151 @@
package com.fatapp.oxygentoolbox.util;
import java.math.BigDecimal;
import java.util.regex.Pattern;
public class HexConversionUtils {
private static int handleLetter(String letter) {
return Integer.parseInt(letter, 36);
}
private static String handleNumber(int number) {
return Integer.toString(number, 36);
}
public static String getRegExp(int base) {
String regExp;
if (base <= 10) {
regExp = "^[+\\-]?[0-" + (base - 1) + "]*(?:[0-" + (base - 1) + "][.][0-" + (base - 1) + "]*)?";
} else {
String letter = Integer.toString(base - 1, 36);
regExp = "^[+\\-]?[0-9a-" + letter + "]*(?:[0-9a-" + letter + "][.][0-9a-" + letter + "]*)?";
}
return regExp;
}
public static String getStrictRegExp(int base) {
String regExp;
if (base <= 10) {
regExp = "^[+\\-]?[0-" + (base - 1) + "]+(?:[.][0-" + (base - 1) + "]+)?$";
} else {
String letter = Integer.toString(base - 1, 36);
regExp = "^[+\\-]?[0-9a-" + letter + "]+(?:[.][0-9a-" + letter + "]+)?$";
}
return regExp;
}
public static boolean checkNumber(String number, int base) {
return Pattern.matches(getStrictRegExp(base), number);
}
private static String baseToDecimal_(String number, int base) {
if (base == 10) {
return number;
}
BigDecimal result = new BigDecimal(0);
String[] data = new StringBuilder(number).reverse().toString().split("");
for (int i = 0; i < data.length; i++) {
result = result.add(BigDecimal.valueOf(handleLetter(data[i]) * Math.pow(base, i)));
}
return result.toString().split("\\.")[0];
}
private static String decimalToBase_(String decimal, int base) {
if (base == 10) {
return decimal;
}
StringBuilder result = new StringBuilder();
BigDecimal quotient = new BigDecimal(decimal);
while (quotient.compareTo(new BigDecimal(base)) > -1) {
BigDecimal[] divideAndRemainder = quotient.divideAndRemainder(new BigDecimal(base));
quotient = divideAndRemainder[0];
BigDecimal remainder = divideAndRemainder[1];
result.insert(0, handleNumber(remainder.intValue()));
}
result.insert(0, handleNumber(quotient.intValue()));
return result.toString();
}
private static String _baseToDecimal(String number, int base) {
if (base == 10) {
return number;
}
BigDecimal result = new BigDecimal(0);
String[] data = number.split("");
for (int i = 0; i < data.length; i++) {
result = result.add(BigDecimal.valueOf(handleLetter(data[i]) * Math.pow(base, -1 * (i + 1))));
}
return result.toString().split("\\.")[1];
}
private static String _decimalToBase(String decimal, int base) {
if (base == 10) {
return decimal;
}
BigDecimal bigDecimal = new BigDecimal("0." + decimal);
StringBuilder result = new StringBuilder();
while (bigDecimal.compareTo(new BigDecimal(0)) > 0 && result.length() < 50) {
bigDecimal = bigDecimal.multiply(new BigDecimal(base));
if (bigDecimal.toString().contains("E")) {
break;
}
if (bigDecimal.toString().contains(".99999")) {
result.append(handleNumber(Integer.parseInt(bigDecimal.toString().split("\\.")[0]) + 1));
break;
}
result.append(handleNumber(Integer.parseInt(bigDecimal.toString().split("\\.")[0])));
bigDecimal = new BigDecimal("0." + bigDecimal.toString().split("\\.")[1]);
}
return result.toString();
}
public static String baseToDecimal(String number, int base) throws Exception{
if (!checkNumber(number, base)) {
throw new Exception("Illegal number");
}
StringBuilder result = new StringBuilder();
String sign = "";
if (number.charAt(0) == '-' || number.charAt(0) == '+') {
sign = number.substring(0, 1);
number = number.substring(1);
}
if (!number.contains(".")) {
result.append(baseToDecimal_(number, base));
} else {
String[] data = number.split("\\.");
result.append(baseToDecimal_(data[0], base));
result.append(".");
result.append(_baseToDecimal(data[1], base));
}
result.insert(0, sign);
return result.toString();
}
public static String decimalToBase(String number, int base) {
StringBuilder result = new StringBuilder();
String sign = "";
if (number.charAt(0) == '-' || number.charAt(0) == '+') {
sign = number.substring(0, 1);
number = number.substring(1);
}
if (!number.contains(".")) {
result.append(decimalToBase_(number, base));
} else {
String[] data = number.split("\\.");
result.append(decimalToBase_(data[0], base));
result.append(".");
result.append(_decimalToBase(data[1], base));
}
result.insert(0, sign);
return result.toString();
}
}

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56dp"
android:height="56dp"
android:autoMirrored="true"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/app_theme"
android:pathData="M 0 12 C 0 5.374 5.374 0 12 0 C 18.627 0 24 5.374 24 12 C 24 18.627 18.627 24 12 24 C 5.374 24 0 18.627 0 12 Z" />
<path
android:fillColor="?attr/app_on_theme"
android:pathData="M18.67,12l-1.17,-1.17L12.83,15.48V5.33h-1.67v10.14l-4.65,-4.66L5.33,12l6.67,6.67 6.67,-6.67z" />
</vector>

View File

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".tools.converter.MainActivity">
<LinearLayout
android:id="@+id/linear_layout_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/tool_converter_layout_margin"
android:background="@drawable/background_radius"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/text_view_base_from"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/tool_converter_base_10"
android:textColor="?attr/app_theme"
android:textSize="@dimen/tool_converter_text_size" />
<ImageView
android:id="@+id/image_view_swap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Swap"
android:padding="@dimen/tool_converter_clickable_padding"
android:src="@drawable/animation_swap_horizon" />
<TextView
android:id="@+id/text_view_base_to"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/tool_converter_base_2"
android:textColor="?attr/app_theme"
android:textSize="@dimen/tool_converter_text_size" />
</LinearLayout>
<EditText
android:id="@+id/edit_text_from"
android:layout_width="match_parent"
android:layout_height="@dimen/tool_converter_edit_text_view_height"
android:layout_margin="@dimen/tool_converter_layout_margin"
android:autofillHints="text"
android:background="@drawable/background_radius"
android:gravity="start|top"
android:hint="@string/tool_converter_please_enter_text"
android:inputType="text"
android:maxLength="850"
android:paddingHorizontal="@dimen/tool_converter_edit_text_view_padding"
android:paddingTop="@dimen/tool_converter_edit_text_view_padding"
android:paddingBottom="@dimen/tool_converter_edit_text_view_padding_plus"
android:textColor="?attr/app_text_theme"
android:textSize="@dimen/tool_converter_text_size"
app:layout_constraintTop_toBottomOf="@id/linear_layout_base" />
<TextView
android:id="@+id/text_view_to"
android:layout_width="match_parent"
android:layout_height="@dimen/tool_converter_edit_text_view_height"
android:layout_margin="@dimen/tool_converter_layout_margin"
android:autofillHints="text"
android:background="@drawable/background_radius"
android:gravity="start|top"
android:paddingHorizontal="@dimen/tool_converter_edit_text_view_padding"
android:paddingTop="@dimen/tool_converter_edit_text_view_padding_plus"
android:paddingBottom="@dimen/tool_converter_edit_text_view_padding"
android:textColor="?attr/app_text_theme"
android:textIsSelectable="true"
android:textSize="@dimen/tool_converter_text_size"
app:layout_constraintTop_toBottomOf="@id/edit_text_from" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_round"
android:padding="@dimen/tool_converter_clickable_padding"
app:layout_constraintBottom_toTopOf="@id/text_view_to"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_text_from">
<ImageView
android:id="@+id/image_view_convert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="Convert"
android:focusable="true"
android:foreground="@drawable/foreground_ripple"
android:src="@drawable/ic_tool_converter_convert"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

File diff suppressed because one or more lines are too long

View File

@@ -87,4 +87,10 @@
<string name="tool_ip_province">省份</string>
<string name="tool_ip_isp">网络提供商</string>
<string name="tool_ip_query_failed">查询失败,请检查网络连接。</string>
<string name="tool_converter_choose_base">选择进制</string>
<string name="tool_converter_from_base">源进制</string>
<string name="tool_converter_to_base">目标进制</string>
<string name="tool_converter_please_enter_text">请输入内容……</string>
<string name="tool_converter_illegal_number">非法数字</string>
</resources>

View File

@@ -87,4 +87,10 @@
<string name="tool_ip_province">省份</string>
<string name="tool_ip_isp">网络提供商</string>
<string name="tool_ip_query_failed">查询失败,请检查网络连接。</string>
<string name="tool_converter_choose_base">选择进制</string>
<string name="tool_converter_from_base">源进制</string>
<string name="tool_converter_to_base">目标进制</string>
<string name="tool_converter_please_enter_text">请输入内容……</string>
<string name="tool_converter_illegal_number">非法数字</string>
</resources>

View File

@@ -87,4 +87,10 @@
<string name="tool_ip_province">省份</string>
<string name="tool_ip_isp">网络提供商</string>
<string name="tool_ip_query_failed">查询失败,请检查网络连接。</string>
<string name="tool_converter_choose_base">选择进制</string>
<string name="tool_converter_from_base">源进制</string>
<string name="tool_converter_to_base">目标进制</string>
<string name="tool_converter_please_enter_text">请输入内容……</string>
<string name="tool_converter_illegal_number">非法数字</string>
</resources>

View File

@@ -48,4 +48,11 @@
<dimen name="tool_ip_layout_padding">10dp</dimen>
<dimen name="tool_ip_layout_margin">@dimen/common_layout_margin</dimen>
<dimen name="tool_ip_query_bar_size">48dp</dimen>
<dimen name="tool_converter_text_size">16sp</dimen>
<dimen name="tool_converter_clickable_padding">5dp</dimen>
<dimen name="tool_converter_layout_margin">@dimen/common_layout_margin</dimen>
<dimen name="tool_converter_edit_text_view_height">200dp</dimen>
<dimen name="tool_converter_edit_text_view_padding">15dp</dimen>
<dimen name="tool_converter_edit_text_view_padding_plus">30dp</dimen>
</resources>

View File

@@ -3,6 +3,8 @@
<string name="app_name">Oxygen</string>
<string name="app_full_name">Oxygen Toolbox</string>
<string name="app_desc">All in One</string>
<string name="base_nan" translatable="false">NaN</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
@@ -123,4 +125,14 @@
<string name="tool_ip_province">Province</string>
<string name="tool_ip_isp">ISP</string>
<string name="tool_ip_query_failed">Query failed, please check network connection.</string>
<string name="tool_converter_base_2" translatable="false">2 (binary)</string>
<string name="tool_converter_base_8" translatable="false">8 (octal)</string>
<string name="tool_converter_base_10" translatable="false">10 (decimal)</string>
<string name="tool_converter_base_16" translatable="false">16 (hex)</string>
<string name="tool_converter_choose_base">Choose base</string>
<string name="tool_converter_from_base">From Base</string>
<string name="tool_converter_to_base">To Base</string>
<string name="tool_converter_please_enter_text">Please enter text…</string>
<string name="tool_converter_illegal_number">Illegal Number</string>
</resources>

View File

@@ -0,0 +1,14 @@
package com.fatapp.oxygentoolbox;
import static org.junit.Assert.assertEquals;
import com.fatapp.oxygentoolbox.util.HexConversionUtils;
import org.junit.Test;
public class MethodTest {
@Test
public void decimalToBaseTest() {
assertEquals(HexConversionUtils.decimalToBase("2.2", 2), "10.00110011001100110011001100110011001100110011001100");
}
}

View File

@@ -1,14 +1,21 @@
package com.fatapp.oxygentoolbox;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class Tester {
public class TempTest {
@Test
public void urlEncodeTest() throws UnsupportedEncodingException {
System.out.println(URLEncoder.encode(" ", StandardCharsets.UTF_8.toString()));
}
@Test
public void hexTest() {
assertEquals(Integer.parseInt("a", 36), 10);
}
}