├── _config.yml
├── lint.xml
├── ArscEditor.apk
├── libs
├── json_simple.jar
└── microsoft_translator_api.jar
├── res
├── drawable-hdpi
│ ├── ic_save.png
│ ├── ic_launcher.png
│ ├── ic_search.png
│ ├── ic_next_page.png
│ ├── ic_prev_page.png
│ └── ic_translate.png
├── values
│ ├── styles.xml
│ ├── dimens.xml
│ ├── array.xml
│ └── strings.xml
└── layout
│ ├── res_string_item.xml
│ ├── translate.xml
│ └── string_list.xml
├── gen
└── zhao
│ └── arsceditor
│ ├── BuildConfig.java
│ └── R.java
├── src
└── zhao
│ └── arsceditor
│ ├── CatchError
│ ├── CrashApplication.java
│ └── CrashHandler.java
│ ├── ResDecoder
│ ├── data
│ │ ├── value
│ │ │ ├── ResStringValue.java
│ │ │ ├── ResValue.java
│ │ │ ├── ResIntBasedValue.java
│ │ │ ├── ResColorValue.java
│ │ │ ├── ResDimenValue.java
│ │ │ ├── ResFractionValue.java
│ │ │ ├── ResFloatValue.java
│ │ │ ├── ResBoolValue.java
│ │ │ ├── ResIdValue.java
│ │ │ ├── ResReferenceValue.java
│ │ │ ├── ResIntValue.java
│ │ │ ├── ResBagValue.java
│ │ │ ├── ResFileValue.java
│ │ │ ├── ResStyleValue.java
│ │ │ ├── ResScalarValue.java
│ │ │ ├── ResEnumAttr.java
│ │ │ ├── ResArrayValue.java
│ │ │ ├── ResValueFactory.java
│ │ │ ├── ResFlagsAttr.java
│ │ │ └── ResAttr.java
│ │ ├── ResUnknownFiles.java
│ │ ├── ResResource.java
│ │ ├── ResID.java
│ │ ├── ResConfig.java
│ │ ├── ResType.java
│ │ ├── ResTypeSpec.java
│ │ ├── ResValuesFile.java
│ │ ├── ResResSpec.java
│ │ ├── ResTable.java
│ │ ├── ResPackage.java
│ │ └── ResConfigFlags.java
│ ├── ARSCCallBack.java
│ ├── GetResValues.java
│ ├── IO
│ │ ├── Duo.java
│ │ ├── LEDataOutputStream.java
│ │ └── LEDataInputStream.java
│ ├── AXMLDecoder.java
│ └── StringBlock.java
│ ├── Translate
│ ├── BingTranslate.java
│ ├── BaiduTranslate.java
│ └── DoTranslate.java
│ ├── SearchString.java
│ ├── AndrolibResources.java
│ └── MainActivity.java
├── .classpath
├── project.properties
├── .settings
└── org.eclipse.jdt.core.prefs
├── proguard-project.txt
├── .project
├── README.md
├── AndroidManifest.xml
└── LICENSE
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/ArscEditor.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/ArscEditor.apk
--------------------------------------------------------------------------------
/libs/json_simple.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/libs/json_simple.jar
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_save.png
--------------------------------------------------------------------------------
/libs/microsoft_translator_api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/libs/microsoft_translator_api.jar
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_search.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_next_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_next_page.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_prev_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_prev_page.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_translate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ha1vk/ArscEditor/HEAD/res/drawable-hdpi/ic_translate.png
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gen/zhao/arsceditor/BuildConfig.java:
--------------------------------------------------------------------------------
1 | /** Automatically generated file. DO NOT MODIFY */
2 | package zhao.arsceditor;
3 |
4 | public final class BuildConfig {
5 | public final static boolean DEBUG = true;
6 | }
--------------------------------------------------------------------------------
/src/zhao/arsceditor/CatchError/CrashApplication.java:
--------------------------------------------------------------------------------
1 | package zhao.arsceditor.CatchError;
2 |
3 | import android.app.Application;
4 |
5 | public class CrashApplication extends Application {
6 |
7 | @Override
8 | public void onCreate() {
9 | // TODO Auto-generated method stub
10 | super.onCreate();
11 | CrashHandler crashHandler = CrashHandler.getInstance();
12 | crashHandler.init(getApplicationContext());
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResStringValue.java:
--------------------------------------------------------------------------------
1 | package zhao.arsceditor.ResDecoder.data.value;
2 |
3 | public class ResStringValue extends ResScalarValue {
4 |
5 | public ResStringValue(String value, int rawValue) {
6 | this(value, rawValue, "string");
7 | }
8 |
9 | public ResStringValue(String value, int rawValue, String type) {
10 | super(type, rawValue, value);
11 | }
12 |
13 | @Override
14 | public String encodeAsResValue() {
15 | return mRawValue;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 60.0px
4 | 48.0px
5 | 20.0px
6 | 16.0px
7 | 6.0px
8 | 3.0px
9 | 6.0px
10 | 3.0px
11 | 84.0px
12 |
13 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-26
15 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.7
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.7
12 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/ARSCCallBack.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * 一个资源解析回调接口
18 | */
19 | package zhao.arsceditor.ResDecoder;
20 |
21 | public interface ARSCCallBack {
22 | void back(String config, String type, String key, String value);
23 | }
24 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class ResValue {
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ArscEditor
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/res/layout/res_string_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
25 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResIntBasedValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | /**
20 | * @author Matt Mastracci
21 | */
22 | public class ResIntBasedValue extends ResValue {
23 | private int mRawIntValue;
24 |
25 | protected ResIntBasedValue(int rawIntValue) {
26 | mRawIntValue = rawIntValue;
27 | }
28 |
29 | public int getRawIntValue() {
30 | return mRawIntValue;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResColorValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class ResColorValue extends ResIntValue {
23 | public ResColorValue(int value, String rawValue) {
24 | super(value, rawValue, "color");
25 | }
26 |
27 | @Override
28 | protected String encodeAsResValue() {
29 | return String.format("#%08x", mValue);
30 | }
31 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### ArscEditor
2 |
3 | [](https://github.com/seaase/ArscEditor/blob/master/LICENSE)
4 |
5 | It is a tool for editing android's arsc file.This tool is written in Java.You can use it to edit strings and the arsc's package name, etc.
6 |
7 | Now it's opened to public.I hope the friends around the world could make contribution for Open Source together.
8 |
9 | It is NOT intended for piracy and other non-legal uses. It could be used for localizing, adding some features or support for custom platforms and other GOOD purposes. Just try to be fair with authors of an app, that you use and probably like.
10 |
11 |
12 | #### IDE of Choice
13 |
14 | [](http://www.eclipse.org/)
15 |
16 | #### Vulnerabilities
17 |
18 | If you discover a vulnerability within ArscEditor, please send an e-mail to ZhaoHai at 2801045898@qq.com.
19 |
20 | #### Links
21 | - [ApkTools](https://github.com/iBotPeaches/Apktool)
22 |
23 | ### Arsc编辑器
24 | 这是一个用于编辑安卓Arsc文件的工具,使用Java语言编写,你可以用它编辑字符串和包名等等。
25 |
26 | 现将其开放,希望世界各地的朋友们一起努力为开源贡献。
27 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/GetResValues.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Ryszard Wiśniewski
3 | * Modified Copyright 2015 ZhaoHai <2801045898@qq.com>
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package zhao.arsceditor.ResDecoder;
19 |
20 | import java.io.IOException;
21 |
22 | import zhao.arsceditor.ResDecoder.data.ResResource;
23 |
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | * @author ZhaoHai
28 | */
29 | public interface GetResValues {
30 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException;
31 | }
32 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResDimenValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import android.util.TypedValue;
20 |
21 | /**
22 | * @author Ryszard Wiśniewski
23 | */
24 | public class ResDimenValue extends ResIntValue {
25 | public ResDimenValue(int value, String rawValue) {
26 | super(value, rawValue, "dimen");
27 | }
28 |
29 | @Override
30 | protected String encodeAsResValue() {
31 | return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResFractionValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import android.util.TypedValue;
20 |
21 | /**
22 | * @author Ryszard Wiśniewski
23 | */
24 | public class ResFractionValue extends ResIntValue {
25 | public ResFractionValue(int value, String rawValue) {
26 | super(value, rawValue, "fraction");
27 | }
28 |
29 | @Override
30 | protected String encodeAsResValue() {
31 | return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/res/values/array.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - 翻译
6 |
7 |
8 | - 百度翻译
9 | - 必应翻译
10 |
11 |
12 | - 自动识别
13 | - 中文
14 | - 英语
15 | - 日语
16 | - 韩语
17 | - 法语
18 | - 泰语
19 | - 俄语
20 | - 德语
21 | - 希腊语
22 | - 意大利语
23 | - 西班牙语
24 | - 葡萄牙语
25 | - 阿拉伯语
26 |
27 |
28 | - auto
29 | - zh
30 | - en
31 | - jp
32 | - kor
33 | - fra
34 | - th
35 | - ru
36 | - de
37 | - el
38 | - it
39 | - spa
40 | - pt
41 | - ara
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResUnknownFiles.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package zhao.arsceditor.ResDecoder.data;
17 |
18 | import java.util.LinkedHashMap;
19 | import java.util.Map;
20 |
21 | /**
22 | * @author Connor Tumbleson
23 | */
24 | public class ResUnknownFiles {
25 |
26 | private final Map mUnknownFiles = new LinkedHashMap();
27 |
28 | public void addUnknownFileInfo(String file, String value) {
29 | mUnknownFiles.put(file, value);
30 | }
31 |
32 | public Map getUnknownFiles() {
33 | return mUnknownFiles;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResFloatValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class ResFloatValue extends ResScalarValue {
23 | private final float mValue;
24 |
25 | public ResFloatValue(float value, int rawIntValue, String rawValue) {
26 | super("float", rawIntValue, rawValue);
27 | this.mValue = value;
28 | }
29 |
30 | @Override
31 | protected String encodeAsResValue() {
32 | return String.valueOf(mValue);
33 | }
34 |
35 | public float getValue() {
36 | return mValue;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResBoolValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class ResBoolValue extends ResScalarValue {
23 | private final boolean mValue;
24 |
25 | public ResBoolValue(boolean value, int rawIntValue, String rawValue) {
26 | super("bool", rawIntValue, rawValue);
27 | this.mValue = value;
28 | }
29 |
30 | @Override
31 | protected String encodeAsResValue() {
32 | return mValue ? "true" : "false";
33 | }
34 |
35 | public boolean getValue() {
36 | return mValue;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ArscEditor
4 | 确定
5 | 取消
6 | 错误
7 | 操作成功
8 | 操作失败
9 | 正在解析...
10 | 翻译
11 | 翻译商:
12 | 正在翻译...
13 | 翻译成功!
14 | 源语言:
15 | 目标语言:
16 | 跳过已翻译的内容
17 | 搜索
18 | 搜索中...
19 | 搜索结果:
20 | 输入需要搜索的内容
21 | 字符串编辑
22 | 没有可供编辑的字符串
23 | 无法编辑!
24 | 保存中...
25 | 内存溢出!
26 | 糟糕,程序已崩溃,请将存储卡目录下的CrashLog.log发送给开发者,以进行修复
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResIdValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.data.ResResource;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResIdValue extends ResValue implements GetResValues {
29 | @Override
30 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
31 | back.back(res.getConfig().toString(), res.getResSpec().getType().getName(), res.getResSpec().getName(),
32 | res.getValue().toString());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResReferenceValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.data.ResPackage;
22 |
23 | /**
24 | * @author Ryszard Wiśniewski
25 | */
26 | public class ResReferenceValue extends ResIntValue {
27 | public ResReferenceValue(ResPackage package_, int value, String rawValue) {
28 | this(package_, value, rawValue, false);
29 | }
30 |
31 | public ResReferenceValue(ResPackage package_, int value, String rawValue, boolean theme) {
32 | super(value, rawValue, "reference");
33 | }
34 |
35 | @Override
36 | protected String encodeAsResValue() throws IOException {
37 | return String.valueOf(mValue);
38 | }
39 |
40 | public boolean isNull() {
41 | return mValue == 0;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResIntValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import android.util.TypedValue;
22 |
23 | /**
24 | * @author Ryszard Wiśniewski
25 | */
26 | public class ResIntValue extends ResScalarValue {
27 | protected final int mValue;
28 | private int type;
29 |
30 | public ResIntValue(int value, String rawValue, int type) {
31 | this(value, rawValue, "integer");
32 | this.type = type;
33 | }
34 |
35 | public ResIntValue(int value, String rawValue, String type) {
36 | super(type, value, rawValue);
37 | this.mValue = value;
38 | }
39 |
40 | @Override
41 | protected String encodeAsResValue() throws IOException {
42 | return TypedValue.coerceToString(type, mValue);
43 | }
44 |
45 | public int getValue() {
46 | return mValue;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/zhao/arsceditor/Translate/BingTranslate.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.Translate;
18 |
19 | import com.memetix.mst.language.Language;
20 | import com.memetix.mst.translate.Translate;
21 |
22 | public class BingTranslate {
23 |
24 | // 源语言
25 | private Language SRCLANGUAGE = Language.ENGLISH;
26 | // 目标语言
27 | private Language TARGETLANGUAGE = Language.CHINESE_SIMPLIFIED;
28 |
29 | // 构造函数
30 | public BingTranslate(Language from, Language to) {
31 | // 获取源语言
32 | SRCLANGUAGE = from;
33 | // 获取目标语言
34 | TARGETLANGUAGE = to;
35 | // 设置翻译api的用户id
36 | Translate.setClientId("20000227");
37 | // 设置翻译api的用户密钥
38 | Translate.setClientSecret("bvgP0SOFq1up2Elv2I8QI1Yuhdb0GZlQ82mS0cDohgM=");
39 | Translate.setHttpReferrer("https://datamarket.azure.com/developer/applications");
40 | }
41 |
42 | /** 获取翻译结果 */
43 | public String getTranslateResult(String str) throws Exception {
44 | return Translate.execute(str, SRCLANGUAGE, TARGETLANGUAGE);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/IO/Duo.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2010 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.IO;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class Duo {
23 | public final T1 m1;
24 | public final T2 m2;
25 |
26 | public Duo(T1 t1, T2 t2) {
27 | this.m1 = t1;
28 | this.m2 = t2;
29 | }
30 |
31 | @Override
32 | public boolean equals(Object obj) {
33 | if (obj == null) {
34 | return false;
35 | }
36 | if (getClass() != obj.getClass()) {
37 | return false;
38 | }
39 | @SuppressWarnings("unchecked")
40 | final Duo other = (Duo) obj;
41 | if (this.m1 != other.m1 && (this.m1 == null || !this.m1.equals(other.m1))) {
42 | return false;
43 | }
44 | if (this.m2 != other.m2 && (this.m2 == null || !this.m2.equals(other.m2))) {
45 | return false;
46 | }
47 | return true;
48 | }
49 |
50 | @Override
51 | public int hashCode() {
52 | int hash = 3;
53 | hash = 71 * hash + (this.m1 != null ? this.m1.hashCode() : 0);
54 | hash = 71 * hash + (this.m2 != null ? this.m2.hashCode() : 0);
55 | return hash;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResBagValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.IO.Duo;
24 | import zhao.arsceditor.ResDecoder.data.ResResource;
25 |
26 | /**
27 | * @author Ryszard Wiśniewski
28 | */
29 | public class ResBagValue extends ResValue implements GetResValues {
30 | protected final ResReferenceValue mParent;
31 |
32 | public ResBagValue(ResReferenceValue parent) {
33 | this.mParent = parent;
34 | }
35 |
36 | public ResReferenceValue getParent() {
37 | return mParent;
38 | }
39 |
40 | @SuppressWarnings("unchecked")
41 | @Override
42 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
43 | String type = res.getResSpec().getType().getName();
44 | if ("style".equals(type)) {
45 | new ResStyleValue(mParent, new Duo[0], null).getResValues(back, res);
46 | return;
47 | }
48 | if ("array".equals(type)) {
49 | new ResArrayValue(mParent, new Duo[0]).getResValues(back, res);
50 | return;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResFileValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.data.ResResource;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResFileValue extends ResIntBasedValue implements GetResValues {
29 | private final String mPath;
30 |
31 | public ResFileValue(String path, int rawIntValue) {
32 | super(rawIntValue);
33 | this.mPath = path;
34 | }
35 |
36 | public String getPath() {
37 | return mPath;
38 | }
39 |
40 | @Override
41 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
42 | back.back(res.getConfig().toString(), res.getResSpec().getType().getName(), res.getResSpec().getName(),
43 | getStrippedPath());
44 | }
45 |
46 | public String getStrippedPath() throws IOException {
47 | if (!mPath.startsWith("res/")) {
48 | throw new IOException("File path does not start with \"res/\": " + mPath);
49 | }
50 | return mPath;/* .substring(4); */
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | return mPath;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResResource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.data.value.ResValue;
22 |
23 | /**
24 | * @author Ryszard Wiśniewski
25 | */
26 | public class ResResource {
27 | private final ResType mConfig;
28 | private final ResResSpec mResSpec;
29 | private final ResValue mValue;
30 |
31 | public ResResource(ResType config, ResResSpec spec, ResValue value) {
32 | this.mConfig = config;
33 | this.mResSpec = spec;
34 | this.mValue = value;
35 | }
36 |
37 | public ResType getConfig() {
38 | return mConfig;
39 | }
40 |
41 | public String getFilePath() {
42 | return mResSpec.getType().getName() + mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
43 | }
44 |
45 | public ResResSpec getResSpec() {
46 | return mResSpec;
47 | }
48 |
49 | public ResValue getValue() {
50 | return mValue;
51 | }
52 |
53 | public void replace(ResValue value) throws IOException {
54 | ResResource res = new ResResource(mConfig, mResSpec, value);
55 | mConfig.addResource(res, true);
56 | mResSpec.addResource(res, true);
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return getFilePath();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResID.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | /**
20 | * @author Ryszard Wiśniewski
21 | */
22 | public class ResID {
23 | public final int entry;
24 | public final int id;
25 | public final int package_;
26 |
27 | public final int type;
28 |
29 | public ResID(int id) {
30 | this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
31 | }
32 |
33 | public ResID(int package_, int type, int entry) {
34 | this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
35 | }
36 |
37 | public ResID(int package_, int type, int entry, int id) {
38 | this.package_ = (package_ == 0) ? 2 : package_;
39 | this.type = type;
40 | this.entry = entry;
41 | this.id = id;
42 | }
43 |
44 | @Override
45 | public boolean equals(Object obj) {
46 | if (obj == null) {
47 | return false;
48 | }
49 | if (getClass() != obj.getClass()) {
50 | return false;
51 | }
52 | final ResID other = (ResID) obj;
53 | if (this.id != other.id) {
54 | return false;
55 | }
56 | return true;
57 | }
58 |
59 | @Override
60 | public int hashCode() {
61 | int hash = 17;
62 | hash = 31 * hash + this.id;
63 | return hash;
64 | }
65 |
66 | @Override
67 | public String toString() {
68 | return String.format("0x%08x", id);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResStyleValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.IO.Duo;
24 | import zhao.arsceditor.ResDecoder.data.ResResource;
25 |
26 | /**
27 | * @author Ryszard Wiśniewski
28 | */
29 | public class ResStyleValue extends ResBagValue implements GetResValues {
30 | private final Duo[] mItems;
31 |
32 | @SuppressWarnings("unchecked")
33 | ResStyleValue(ResReferenceValue parent, Duo[] items, ResValueFactory factory) {
34 | super(parent);
35 |
36 | mItems = new Duo[items.length];
37 | for (int i = 0; i < items.length; i++) {
38 | mItems[i] = new Duo(factory.newReference(items[i].m1, null),
39 | items[i].m2);
40 | }
41 | }
42 |
43 | @Override
44 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
45 | for (int i = 0; i < mItems.length; i++) {
46 | Duo item = mItems[i];
47 | back.back(res.getConfig().toString(), res.getResSpec().getType().getName(), res.getResSpec().getName(),
48 | item.m2.encodeResValue());
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResScalarValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.data.ResResource;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public abstract class ResScalarValue extends ResIntBasedValue implements GetResValues {
29 | protected final String mRawValue;
30 | protected final String mType;
31 |
32 | protected ResScalarValue(String type, int rawIntValue, String rawValue) {
33 | super(rawIntValue);
34 | mType = type;
35 | mRawValue = rawValue;
36 | }
37 |
38 | protected abstract String encodeAsResValue() throws IOException;
39 |
40 | public String encodeAsResXmlItemValue() throws IOException {
41 | return encodeResValue();
42 | }
43 |
44 | public String encodeResValue() throws IOException {
45 | if (mRawValue != null) {
46 | return mRawValue;
47 | }
48 | return encodeAsResValue();
49 | }
50 |
51 | @Override
52 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
53 | String type = res.getResSpec().getType().getName();
54 |
55 | String body = encodeAsResValue();
56 | back.back(res.getConfig().toString(), type, res.getResSpec().getName(), body);
57 | }
58 |
59 | public String getType() {
60 | return mType;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResEnumAttr.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | import android.annotation.SuppressLint;
24 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
25 | import zhao.arsceditor.ResDecoder.IO.Duo;
26 | import zhao.arsceditor.ResDecoder.data.ResResource;
27 |
28 | /**
29 | * @author Ryszard Wiśniewski
30 | */
31 | public class ResEnumAttr extends ResAttr {
32 | private final Duo[] mItems;
33 |
34 | @SuppressLint("UseSparseArrays")
35 | private final Map mItemsCache = new HashMap();
36 |
37 | ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n,
38 | Duo[] items) {
39 | super(parent, type, min, max, l10n);
40 | mItems = items;
41 | }
42 |
43 | @Override
44 | public String convertToResXmlFormat(ResScalarValue value) throws IOException {
45 | if (value instanceof ResIntValue) {
46 | String ret = String.valueOf(value);
47 | if (ret != null) {
48 | return ret;
49 | }
50 | }
51 | return super.convertToResXmlFormat(value);
52 | }
53 |
54 | @Override
55 | protected void serializeBody(ARSCCallBack back, ResResource res) throws IOException, IOException {
56 | for (Duo duo : mItems) {
57 | int intVal = duo.m2.getValue();
58 | back.back(res.getConfig().toString(), "enum", res.getResSpec().getName(), String.valueOf(intVal));
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResConfig.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.LinkedHashMap;
21 | import java.util.LinkedHashSet;
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResConfig {
29 | private final ResConfigFlags mFlags;
30 | private final Map mResources = new LinkedHashMap();
31 |
32 | public ResConfig(ResConfigFlags flags) {
33 | this.mFlags = flags;
34 | }
35 |
36 | public void addResource(ResResource res) throws IOException {
37 | addResource(res, false);
38 | }
39 |
40 | public void addResource(ResResource res, boolean overwrite) throws IOException {
41 | ResResSpec spec = res.getResSpec();
42 | if (mResources.put(spec, res) != null && !overwrite) {
43 | /**
44 | * throw new IOException(String.format( "Multiple resources:
45 | * spec=%s, config=%s", spec, this));
46 | */
47 | }
48 | }
49 |
50 | public ResConfigFlags getFlags() {
51 | return mFlags;
52 | }
53 |
54 | public ResResource getResource(ResResSpec spec) throws IOException {
55 | ResResource res = mResources.get(spec);
56 | if (res == null) {
57 | /*
58 | * throw new IOException(String.format(
59 | * "resource: spec=%s, config=%s", spec, this));
60 | */
61 | }
62 | return res;
63 | }
64 |
65 | public Set listResources() {
66 | return new LinkedHashSet(mResources.values());
67 | }
68 |
69 | public Set listResSpecs() {
70 | return mResources.keySet();
71 | }
72 |
73 | @Override
74 | public String toString() {
75 | return mFlags.toString();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/res/layout/translate.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
16 |
20 |
27 |
28 |
32 |
39 |
40 |
44 |
51 |
52 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResType.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.LinkedHashMap;
21 | import java.util.LinkedHashSet;
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResType {
29 | private final ResConfigFlags mFlags;
30 | private final Map mResources = new LinkedHashMap();
31 |
32 | public ResType(ResConfigFlags flags) {
33 | this.mFlags = flags;
34 | }
35 |
36 | public void addResource(ResResource res) throws IOException {
37 | addResource(res, false);
38 | }
39 |
40 | public void addResource(ResResource res, boolean overwrite) throws IOException {
41 | ResResSpec spec = res.getResSpec();
42 | if (mResources.put(spec, res) != null && !overwrite) {
43 | // throw new IOException(String.format("Multiple resources: spec=%s,
44 | // config=%s", spec, this));
45 | }
46 | }
47 |
48 | public ResConfigFlags getFlags() {
49 | return mFlags;
50 | }
51 |
52 | public ResResource getResource(ResResSpec spec) throws IOException {
53 | ResResource res = mResources.get(spec);
54 | if (res == null) {
55 | // throw new UndefinedResObject(String.format("resource: spec=%s,
56 | // config=%s", spec, this));
57 | }
58 | return res;
59 | }
60 |
61 | public Set listResources() {
62 | return new LinkedHashSet(mResources.values());
63 | }
64 |
65 | public Set listResSpecs() {
66 | return mResources.keySet();
67 | }
68 |
69 | public void removeResource(ResResource res) throws IOException {
70 | ResResSpec spec = res.getResSpec();
71 | mResources.remove(spec);
72 | }
73 |
74 | @Override
75 | public String toString() {
76 | return mFlags.toString();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResTypeSpec.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.LinkedHashMap;
21 | import java.util.LinkedHashSet;
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public final class ResTypeSpec {
29 | private final int mEntryCount;
30 | private final byte mId;
31 |
32 | private final String mName;
33 | private final Map mResSpecs = new LinkedHashMap();
34 |
35 | public ResTypeSpec(String name, ResTable resTable, ResPackage package_, byte id, int entryCount) {
36 | this.mName = name;
37 | this.mId = id;
38 | this.mEntryCount = entryCount;
39 | }
40 |
41 | public void addResSpec(ResResSpec spec) throws IOException {
42 | if (mResSpecs.put(spec.getName(), spec) != null) {
43 | // throw new IOException(String.format("Multiple res specs: %s/%s",
44 | // getName(), spec.getName()));
45 | }
46 | }
47 |
48 | public int getEntryCount() {
49 | return mEntryCount;
50 | }
51 |
52 | public byte getId() {
53 | return mId;
54 | }
55 |
56 | public String getName() {
57 | return mName;
58 | }
59 |
60 | public ResResSpec getResSpec(String name) throws IOException {
61 | ResResSpec spec = mResSpecs.get(name);
62 | if (spec == null) {
63 | // throw new UndefinedResObject(String.format("resource spec:
64 | // %s/%s", getName(), name));
65 | }
66 | return spec;
67 | }
68 |
69 | public boolean isString() {
70 | return mName.equalsIgnoreCase("string");
71 | }
72 |
73 | public Set listResSpecs() {
74 | return new LinkedHashSet(mResSpecs.values());
75 | }
76 |
77 | public void removeResSpec(ResResSpec spec) throws IOException {
78 | mResSpecs.remove(spec.getName());
79 | }
80 |
81 | @Override
82 | public String toString() {
83 | return mName;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResValuesFile.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.util.LinkedHashSet;
20 | import java.util.Set;
21 |
22 | /**
23 | * @author Ryszard Wiśniewski
24 | */
25 | public class ResValuesFile {
26 | private final ResType mConfig;
27 | private final ResPackage mPackage;
28 | private final Set mResources = new LinkedHashSet();
29 | private final ResTypeSpec mType;
30 |
31 | public ResValuesFile(ResPackage pkg, ResTypeSpec type, ResType config) {
32 | this.mPackage = pkg;
33 | this.mType = type;
34 | this.mConfig = config;
35 | }
36 |
37 | public void addResource(ResResource res) {
38 | mResources.add(res);
39 | }
40 |
41 | @Override
42 | public boolean equals(Object obj) {
43 | if (obj == null) {
44 | return false;
45 | }
46 | if (getClass() != obj.getClass()) {
47 | return false;
48 | }
49 | final ResValuesFile other = (ResValuesFile) obj;
50 | if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) {
51 | return false;
52 | }
53 | if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
54 | return false;
55 | }
56 | return true;
57 | }
58 |
59 | public ResType getConfig() {
60 | return mConfig;
61 | }
62 |
63 | public String getPath() {
64 | return "values" + mConfig.getFlags().getQualifiers() + "/" + mType.getName()
65 | + (mType.getName().endsWith("s") ? "" : "s") + ".xml";
66 | }
67 |
68 | public ResTypeSpec getType() {
69 | return mType;
70 | }
71 |
72 | @Override
73 | public int hashCode() {
74 | int hash = 17;
75 | hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
76 | hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
77 | return hash;
78 | }
79 |
80 | public boolean isSynthesized(ResResource res) {
81 | return mPackage.isSynthesized(res.getResSpec().getId());
82 | }
83 |
84 | public Set listResources() {
85 | return mResources;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResArrayValue.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 | import java.util.Arrays;
21 |
22 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
23 | import zhao.arsceditor.ResDecoder.GetResValues;
24 | import zhao.arsceditor.ResDecoder.IO.Duo;
25 | import zhao.arsceditor.ResDecoder.data.ResResource;
26 |
27 | /**
28 | * @author Ryszard Wiśniewski
29 | */
30 | public class ResArrayValue extends ResBagValue implements GetResValues {
31 | public static final int BAG_KEY_ARRAY_START = 0x02000000;
32 |
33 | private final String AllowedArrayTypes[] = { "string", "integer" };
34 |
35 | private final ResScalarValue[] mItems;
36 |
37 | ResArrayValue(ResReferenceValue parent, Duo[] items) {
38 | super(parent);
39 |
40 | mItems = new ResScalarValue[items.length];
41 | for (int i = 0; i < items.length; i++) {
42 | mItems[i] = items[i].m2;
43 | }
44 | }
45 |
46 | public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) {
47 | super(parent);
48 | mItems = items;
49 | }
50 |
51 | @Override
52 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
53 | String type = getType();
54 | type = (type == null ? "" : type + "-") + "array";
55 | // add - 's
56 | for (int i = 0; i < mItems.length; i++) {
57 | back.back(res.getConfig().toString(), type, res.getResSpec().getName(), mItems[i].encodeAsResValue());
58 | }
59 | }
60 |
61 | public String getType() throws IOException {
62 | if (mItems.length == 0) {
63 | return null;
64 | }
65 | String type = mItems[0].getType();
66 | for (int i = 0; i < mItems.length; i++) {
67 | if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) {
68 | return "string";
69 | } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) {
70 | return null;
71 | } else if (mItems[i].encodeAsResXmlItemValue().startsWith("@integer")) {
72 | return "integer";
73 | } else if (!"string".equals(type) && !"integer".equals(type)) {
74 | return null;
75 | } else if (!type.equals(mItems[i].getType())) {
76 | return null;
77 | }
78 | }
79 | if (!Arrays.asList(AllowedArrayTypes).contains(type)) {
80 | return "string";
81 | }
82 | return type;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/Translate/BaiduTranslate.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.Translate;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.io.InputStreamReader;
23 | import java.net.URL;
24 | import java.net.URLConnection;
25 | import java.net.URLEncoder;
26 |
27 | import org.json.JSONArray;
28 | import org.json.JSONException;
29 | import org.json.JSONObject;
30 | import org.json.JSONTokener;
31 |
32 | import android.util.Log;
33 |
34 | public class BaiduTranslate {
35 |
36 | // 需要翻译的内容,源语言,目标语言
37 | private String str = null, fromString = "auto", toString = "auto";
38 | // 结果
39 | private String result = "";
40 |
41 | // 构造函数
42 | public BaiduTranslate(String fromString, String toString) {
43 | // 获取源语言
44 | this.fromString = fromString;
45 | // 获取目标语言
46 | this.toString = toString;
47 | }
48 |
49 | /**
50 | * 获取翻译结果 str:需要翻译的内容
51 | */
52 | public String getResult(String str) throws IOException, JSONException {
53 | // 获取需要翻译的内容
54 | this.str = str;
55 | // 开启翻译
56 | doTranslate();
57 | // 返回结果
58 | return result;
59 | }
60 |
61 | // 翻译的函数
62 | public void doTranslate() throws IOException, JSONException {
63 |
64 | // 格式化需要翻译的内容为UTF-8编码
65 | String str_utf = URLEncoder.encode(str, "UTF-8");
66 | // 百度翻译api
67 | String str_url = "http://openapi.baidu.com/public/2.0/bmt/translate?client_id=GOr7jiTs5hiQvkHqDNg4KSTV&q="
68 | + str_utf + "&from=" + fromString + "&to=" + toString;
69 | // 将api网址转化成URL
70 | URL url_word = new URL(str_url);
71 | // 连接到该URL
72 | URLConnection connection = (URLConnection) url_word.openConnection();
73 | // 获取输入流
74 | InputStream is = connection.getInputStream();
75 | // 转化成读取流
76 | InputStreamReader isr = new InputStreamReader(is);
77 | // 转化成缓冲读取流
78 | BufferedReader br = new BufferedReader(isr);
79 | // 每行的内容
80 | String line;
81 | // 字符串处理类
82 | StringBuilder sBuilder = new StringBuilder();
83 | // 读取每行内容
84 | while ((line = br.readLine()) != null) {
85 | // 在字符串末尾追加内容
86 | sBuilder.append(line);
87 | }
88 |
89 | /**
90 | * 单词解析
91 | */
92 |
93 | JSONTokener jtk = new JSONTokener(sBuilder.toString());
94 | JSONObject jObject = (JSONObject) jtk.nextValue();
95 |
96 | JSONArray jArray = jObject.getJSONArray("trans_result");
97 | Log.i("TAG", url_word.toString());
98 | Log.i("TAG", jObject.toString());
99 |
100 | JSONObject sub_jObject_1 = jArray.getJSONObject(0);
101 | // dst对应的内容就是翻译结果
102 | result = sub_jObject_1.getString("dst");
103 |
104 | br.close();
105 | isr.close();
106 | is.close();
107 | }
108 | }
--------------------------------------------------------------------------------
/res/layout/string_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
28 |
29 |
30 |
31 |
37 |
38 |
47 |
56 |
65 |
72 |
79 |
80 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResValueFactory.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import android.util.TypedValue;
22 | import zhao.arsceditor.ResDecoder.IO.Duo;
23 | import zhao.arsceditor.ResDecoder.data.ResPackage;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResValueFactory {
29 | private final ResPackage mPackage;
30 |
31 | public ResValueFactory(ResPackage pakage_) {
32 | this.mPackage = pakage_;
33 | }
34 |
35 | public ResBagValue bagFactory(int parent, Duo[] items) throws IOException {
36 | ResReferenceValue parentVal = newReference(parent, null);
37 |
38 | if (items.length == 0) {
39 | return new ResBagValue(parentVal);
40 | }
41 | int key = items[0].m1;
42 | if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
43 | return ResAttr.factory(parentVal, items, this, mPackage);
44 | }
45 | if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
46 | return new ResArrayValue(parentVal, items);
47 | }
48 | return new ResStyleValue(parentVal, items, this);
49 | }
50 |
51 | public ResScalarValue factory(int type, int value, String rawValue) throws IOException {
52 | switch (type) {
53 | case TypedValue.TYPE_NULL:
54 | return new ResReferenceValue(mPackage, 0, null);
55 | case TypedValue.TYPE_REFERENCE:
56 | return newReference(value, rawValue);
57 | case TypedValue.TYPE_ATTRIBUTE:
58 | return newReference(value, rawValue, true);
59 | case TypedValue.TYPE_STRING:
60 | return new ResStringValue(rawValue, value);
61 | case TypedValue.TYPE_FLOAT:
62 | return new ResFloatValue(Float.intBitsToFloat(value), value, rawValue);
63 | case TypedValue.TYPE_DIMENSION:
64 | return new ResDimenValue(value, rawValue);
65 | case TypedValue.TYPE_FRACTION:
66 | return new ResFractionValue(value, rawValue);
67 | case TypedValue.TYPE_INT_BOOLEAN:
68 | return new ResBoolValue(value != 0, value, rawValue);
69 | case 0x07:
70 | return newReference(value, rawValue);
71 | }
72 |
73 | if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) {
74 | return new ResColorValue(value, rawValue);
75 | }
76 | if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) {
77 | return new ResIntValue(value, rawValue, type);
78 | }
79 |
80 | throw new IOException("Invalid value type: " + type);
81 | }
82 |
83 | public ResIntBasedValue factory(String value, int rawValue) {
84 | if (value.startsWith("res/")) {
85 | return new ResFileValue(value, rawValue);
86 | }
87 | return new ResStringValue(value, rawValue);
88 | }
89 |
90 | public ResReferenceValue newReference(int resID, String rawValue) {
91 | return newReference(resID, rawValue, false);
92 | }
93 |
94 | public ResReferenceValue newReference(int resID, String rawValue, boolean theme) {
95 | return new ResReferenceValue(mPackage, resID, rawValue, theme);
96 | }
97 | }
--------------------------------------------------------------------------------
/src/zhao/arsceditor/SearchString.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package zhao.arsceditor;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | import android.app.AlertDialog;
22 | import android.app.Dialog;
23 | import android.app.ProgressDialog;
24 | import android.content.Context;
25 | import android.content.DialogInterface;
26 | import android.os.AsyncTask;
27 | import android.widget.EditText;
28 |
29 | //本类实现在字符常量池中搜索字符串
30 | @SuppressWarnings("deprecation")
31 | public class SearchString {
32 | // 进度条控件
33 | private ProgressDialog mdialog;
34 | // 上下文
35 | private Context mContext;
36 | // 在某个集合中搜索
37 | private List listitems;
38 | // 搜索结果
39 | private List listresult = new ArrayList();
40 | // 储存搜索的结果中每一个条目在原来的集合中的位置
41 | private List listposition = new ArrayList();
42 |
43 | // 构造函数
44 | public SearchString(Context context, List listitems) {
45 | // 获取上下文
46 | mContext = context;
47 | this.listitems = listitems;
48 | }
49 |
50 | // 搜索
51 | public void search() {
52 | final EditText name = new EditText(mContext);
53 | name.setHint(mContext.getString(R.string.search_hint));
54 |
55 | Dialog alertDialog = new AlertDialog.Builder(mContext).setTitle(R.string.search).setView(name)
56 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
57 | @Override
58 | public void onClick(DialogInterface dialog, int which) {
59 | new AsyncLoader().execute(name.getText().toString()); // 执行线程进行搜索
60 | }
61 | }).setNegativeButton(R.string.cancel, null).create();
62 | alertDialog.show();
63 | }
64 |
65 | // 线程
66 | // AsyncTask
67 | class AsyncLoader extends AsyncTask {
68 | @Override
69 | // doInBackground执行前执行
70 | protected void onPreExecute() {
71 | // 创建进度条对象
72 | mdialog = new ProgressDialog(mContext);
73 | // 设置进度条提示的信息
74 | mdialog.setMessage(mContext.getString(R.string.searching));
75 | // 设置点击进度条外,进度条不消失
76 | mdialog.setCancelable(false);
77 | // 设置进度条样式为圆形
78 | mdialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
79 | // 显示进度条
80 | mdialog.show();
81 |
82 | }
83 |
84 | // 正式开始搜索
85 | protected Void doInBackground(String... params) {
86 |
87 | for (String result : listitems) // 遍历获取成员,并筛选
88 | {
89 | if (result.indexOf(params[0]) != -1) {
90 | listresult.add(result);
91 | listposition.add(listitems.indexOf(result));
92 | }
93 | }
94 | return null;
95 | }
96 |
97 | // doInBackground结束后执行
98 | protected void onPostExecute(Void result) {
99 |
100 | mdialog.dismiss();
101 | new AlertDialog.Builder(mContext).setTitle(R.string.search_result)
102 | .setItems((String[]) listresult.toArray(new String[listresult.size()]),
103 | new DialogInterface.OnClickListener() {
104 | @Override
105 | public void onClick(DialogInterface arg0, int arg1) {
106 | // 在原来的列表中选择该条目
107 | ((MainActivity) mContext).stringListView.setSelection(listposition.get(arg1));
108 | }
109 | })
110 | .create().show();
111 |
112 | }
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/AXMLDecoder.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package zhao.arsceditor.ResDecoder;
17 |
18 | import java.io.ByteArrayInputStream;
19 | import java.io.ByteArrayOutputStream;
20 | import java.io.IOException;
21 | import java.io.InputStream;
22 | import java.io.OutputStream;
23 | import java.nio.charset.CharacterCodingException;
24 | import java.util.List;
25 |
26 | import zhao.arsceditor.ResDecoder.IO.LEDataInputStream;
27 | import zhao.arsceditor.ResDecoder.IO.LEDataOutputStream;
28 |
29 | public class AXMLDecoder {
30 |
31 | // AXML文件头
32 | private static final int AXML_CHUNK_TYPE = 0x00080003;
33 | // 字符串常量池类
34 | public StringBlock mTableStrings;
35 | // 二进制文件输入流
36 | private final LEDataInputStream mIn;
37 | // 二进制文件输入流2
38 | private LEDataInputStream mIn2;
39 | // chunkSize
40 | private int chunkSize;
41 | // AXML文件数据的字节数组
42 | private static byte[] bytes;
43 |
44 | // 构造函数
45 | private AXMLDecoder(LEDataInputStream in) {
46 | this.mIn = in;
47 | }
48 |
49 | // 读取字符串
50 | private void readStrings() throws IOException {
51 | // type
52 | int type = mIn.readInt();
53 | // 检查头
54 | checkChunk(type, AXML_CHUNK_TYPE);
55 | // Chunk size
56 | chunkSize = mIn.readInt();
57 | // 读取字符串常量池
58 | mTableStrings = StringBlock.read(this.mIn);
59 | }
60 |
61 | public static AXMLDecoder read(InputStream input) throws IOException {
62 | AXMLDecoder axml = new AXMLDecoder(new LEDataInputStream(input));
63 | axml.readStrings();
64 | bytes = LEDataInputStream.toByteArray(input);
65 | return axml;
66 | }
67 |
68 | public void getStrings(List m_strings) throws CharacterCodingException {
69 | for (int i = 0; i < mTableStrings.getCount(); i++) {
70 | m_strings.add(mTableStrings.getString(i));
71 | }
72 | }
73 |
74 | public void write(List stringlist_src, List stringlist_tar, OutputStream out) throws IOException {
75 | write(stringlist_src, stringlist_tar, new LEDataOutputStream(out));
76 | }
77 |
78 | private void write(List stringlist_src, List stringlist_tar, LEDataOutputStream lmOut)
79 | throws IOException {
80 | // 排序列表中的字符串,以方便一一写入
81 | for (int i = 0; i < stringlist_src.size(); i++)
82 | mTableStrings.sortStringBlock(stringlist_src.get(i), stringlist_tar.get(i));
83 | // 先将字符串数据写入到一个临时的流中
84 | ByteArrayOutputStream mStrings = mTableStrings.writeString(mTableStrings.getList());
85 | // 写入文件头
86 | lmOut.writeInt(AXML_CHUNK_TYPE);
87 | // 写入chunkSize
88 | lmOut.writeInt(chunkSize + (mStrings.size() - mTableStrings.m_strings.length));
89 | // 写入字符串常量池
90 | mTableStrings.writeFully(lmOut, mStrings);
91 | // 创建二进制文件输入流对象2,用来写入余下的内容
92 | mIn2 = new LEDataInputStream(new ByteArrayInputStream(bytes));
93 | // 写入剩下的数据
94 | int num;
95 | while ((num = mIn2.readByte()) != -1)
96 | lmOut.writeByte((byte) num);
97 | }
98 |
99 | private void checkChunk(int type, int expectedType) throws IOException {
100 | if (type != expectedType)
101 | throw new IOException(String.format("Invalid chunk type: expected=0x%08x, got=0x%08x",
102 | new Object[] { Integer.valueOf(expectedType), Short.valueOf((short) type) }));
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/IO/LEDataOutputStream.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /*******************************************************************************
17 | * @(#)LEDataOutputStream.java
18 | * @author zhaohai
19 | * 二进制文件输出处理工具类
20 | * 2015.10
21 | */
22 |
23 | package zhao.arsceditor.ResDecoder.IO;
24 |
25 | import java.io.DataOutputStream;
26 | import java.io.IOException;
27 | import java.io.OutputStream;
28 |
29 | public class LEDataOutputStream {
30 |
31 | /** 二进制文件输出流 */
32 | private DataOutputStream dos;
33 |
34 | /** 构造函数 */
35 | public LEDataOutputStream(OutputStream out) {
36 | dos = new DataOutputStream(out);
37 | }
38 |
39 | /**
40 | * 关闭流
41 | *
42 | * @throws IOException
43 | */
44 | public void close() throws IOException {
45 | dos.flush();
46 | dos.close();
47 | }
48 |
49 | /**
50 | * 获取流的大小
51 | *
52 | * @return
53 | */
54 | public int size() {
55 | return dos.size();
56 | }
57 |
58 | /**
59 | * 写入一个字节
60 | *
61 | * @param b
62 | * @throws IOException
63 | */
64 | public void writeByte(byte b) throws IOException {
65 | dos.writeByte(b);
66 | }
67 |
68 | /**
69 | * 写入length个空字节
70 | *
71 | * @param length
72 | * @throws IOException
73 | */
74 | public void writeBytes(int length) throws IOException {
75 | for (int i = 0; i < length; i++)
76 | dos.writeByte(0);
77 | }
78 |
79 | /**
80 | * 写入字符数组
81 | *
82 | * @param charbuf
83 | * @throws IOException
84 | */
85 | public void writeCharArray(char[] charbuf) throws IOException {
86 | int length = charbuf.length;
87 | for (int i = 0; i < length; i++)
88 | writeShort((short) charbuf[i]);
89 | }
90 |
91 | /**
92 | * 写入字节数组
93 | *
94 | * @param b
95 | * @throws IOException
96 | */
97 | public void writeFully(byte[] b) throws IOException {
98 | dos.write(b, 0, b.length);
99 | }
100 |
101 | /**
102 | * 写入字节数组
103 | *
104 | * @param buffer
105 | * @param offset
106 | * @param count
107 | * @throws IOException
108 | */
109 | public void writeFully(byte[] buffer, int offset, int count) throws IOException {
110 | dos.write(buffer, offset, count);
111 | }
112 |
113 | /**
114 | * 写入一个32位的int型数据
115 | *
116 | * @param i
117 | * @throws IOException
118 | */
119 | public void writeInt(int i) throws IOException {
120 | dos.writeByte(i & 0xff);
121 | dos.writeByte((i >> 8) & 0xff);
122 | dos.writeByte((i >> 16) & 0xff);
123 | dos.writeByte((i >> 24) & 0xff);
124 | }
125 |
126 | /**
127 | * 写入32位的int型数组
128 | *
129 | * @param buf
130 | * @throws IOException
131 | */
132 | public void writeIntArray(int[] buf) throws IOException {
133 | writeIntArray(buf, 0, buf.length);
134 | }
135 |
136 | /**
137 | * 写入32位的int型数组
138 | *
139 | * @param buf
140 | * @param s
141 | * @param end
142 | * @throws IOException
143 | */
144 | private void writeIntArray(int[] buf, int s, int end) throws IOException {
145 | for (; s < end; s++)
146 | writeInt(buf[s]);
147 | }
148 |
149 | /**
150 | * 写入包名
151 | *
152 | * @param name
153 | * @throws IOException
154 | */
155 | public void writeNulEndedString(String name) throws IOException {
156 | char[] ch = name.toCharArray();
157 | int length = ch.length;
158 |
159 | for (int i = 0; i < length; i++)
160 | writeShort((short) ch[i]);
161 |
162 | writeBytes(128 * 2 - length * 2);
163 | }
164 |
165 | /** 写入一个16位的short型数据 */
166 | public void writeShort(short s) throws IOException {
167 | dos.writeByte(s & 0xff);
168 | dos.writeByte((s >>> 8) & 0xff);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResResSpec.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.LinkedHashMap;
21 | import java.util.LinkedHashSet;
22 | import java.util.Map;
23 | import java.util.Set;
24 |
25 | /**
26 | * @author Ryszard Wiśniewski
27 | */
28 | public class ResResSpec {
29 | private final ResID mId;
30 | private final String mName;
31 | private final ResPackage mPackage;
32 | private final Map mResources = new LinkedHashMap();
33 | private final ResTypeSpec mType;
34 |
35 | public ResResSpec(ResID id, String name, ResPackage pkg, ResTypeSpec type) {
36 | this.mId = id;
37 | this.mName = (name.equals("") ? ("APKTOOL_DUMMYVAL_" + id.toString()) : name);
38 | this.mPackage = pkg;
39 | this.mType = type;
40 | }
41 |
42 | public void addResource(ResResource res) throws IOException {
43 | addResource(res, false);
44 | }
45 |
46 | public void addResource(ResResource res, boolean overwrite) throws IOException {
47 | ResConfigFlags flags = res.getConfig().getFlags();
48 | if (mResources.put(flags, res) != null && !overwrite) {
49 | // throw new IOException(String.format("Multiple resources: spec=%s,
50 | // config=%s", this, flags));
51 | }
52 | }
53 |
54 | public ResResource getDefaultResource() throws IOException {
55 | return getResource(new ResConfigFlags());
56 | }
57 |
58 | public String getFullName() {
59 | return getFullName(false, false);
60 | }
61 |
62 | public String getFullName(boolean excludePackage, boolean excludeType) {
63 | return (excludePackage ? "" : getPackage().getName() + ":") + (excludeType ? "" : getType().getName() + "/")
64 | + getName();
65 | }
66 |
67 | public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
68 | return getFullName(getPackage().equals(relativeToPackage), excludeType);
69 | }
70 |
71 | public ResID getId() {
72 | return mId;
73 | }
74 |
75 | public String getName() {
76 | return mName.replace("\"", "q");
77 | }
78 |
79 | public ResPackage getPackage() {
80 | return mPackage;
81 | }
82 |
83 | public ResResource getResource(ResConfigFlags config) throws IOException {
84 | ResResource res = mResources.get(config);
85 | if (res == null) {
86 | // throw new IOException(String.format("resource: spec=%s,
87 | // config=%s", this, config));
88 | }
89 | return res;
90 | }
91 |
92 | public ResResource getResource(ResType config) throws IOException {
93 | return getResource(config.getFlags());
94 | }
95 |
96 | public ResTypeSpec getType() {
97 | return mType;
98 | }
99 |
100 | public boolean hasDefaultResource() {
101 | return mResources.containsKey(new ResConfigFlags());
102 | }
103 |
104 | private boolean hasResource(ResConfigFlags flags) {
105 | return mResources.containsKey(flags);
106 | }
107 |
108 | public boolean hasResource(ResType config) {
109 | return hasResource(config.getFlags());
110 | }
111 |
112 | public boolean isDummyResSpec() {
113 | return getName().startsWith("APKTOOL_DUMMY_");
114 | }
115 |
116 | public Set listResources() {
117 | return new LinkedHashSet(mResources.values());
118 | }
119 |
120 | public void removeResource(ResResource res) throws IOException {
121 | ResConfigFlags flags = res.getConfig().getFlags();
122 | mResources.remove(flags);
123 | }
124 |
125 | @Override
126 | public String toString() {
127 | return mId.toString() + " " + mType.toString() + "/" + mName;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/gen/zhao/arsceditor/R.java:
--------------------------------------------------------------------------------
1 | /* AUTO-GENERATED FILE. DO NOT MODIFY.
2 | *
3 | * This class was automatically generated by the
4 | * aapt tool from the resource data it found. It
5 | * should not be modified by hand.
6 | */
7 |
8 | package zhao.arsceditor;
9 |
10 | public final class R {
11 | public static final class array {
12 | public static final int language=0x7f040002;
13 | public static final int language_short=0x7f040003;
14 | public static final int translate=0x7f040000;
15 | public static final int translate_provider=0x7f040001;
16 | }
17 | public static final class attr {
18 | }
19 | public static final class dimen {
20 | public static final int btn_min_size=0x7f050001;
21 | public static final int icon_size=0x7f050000;
22 | public static final int margin_normal=0x7f050004;
23 | public static final int margin_smaller=0x7f050005;
24 | public static final int padding_normal=0x7f050006;
25 | public static final int padding_smaller=0x7f050007;
26 | public static final int prefered_list_height=0x7f050008;
27 | public static final int text_size_normal=0x7f050002;
28 | public static final int text_size_smaller=0x7f050003;
29 | }
30 | public static final class drawable {
31 | public static final int ic_launcher=0x7f020000;
32 | public static final int ic_next_page=0x7f020001;
33 | public static final int ic_prev_page=0x7f020002;
34 | public static final int ic_save=0x7f020003;
35 | public static final int ic_search=0x7f020004;
36 | public static final int ic_translate=0x7f020005;
37 | }
38 | public static final class id {
39 | public static final int btnSave=0x7f080005;
40 | public static final int btnSearch=0x7f080007;
41 | public static final int btnTranslate=0x7f080006;
42 | public static final int info=0x7f080002;
43 | public static final int linearLayout1=0x7f080004;
44 | public static final int list_res_string=0x7f080003;
45 | public static final int skip_already_translate=0x7f08000d;
46 | public static final int src_type=0x7f08000b;
47 | public static final int textCategory=0x7f080008;
48 | public static final int textConfig=0x7f080009;
49 | public static final int translate_to=0x7f08000c;
50 | public static final int translator=0x7f08000a;
51 | public static final int txtOriginal=0x7f080000;
52 | public static final int txtTranslated=0x7f080001;
53 | }
54 | public static final class layout {
55 | public static final int res_string_item=0x7f030000;
56 | public static final int string_list=0x7f030001;
57 | public static final int translate=0x7f030002;
58 | }
59 | public static final class string {
60 | public static final int app_name=0x7f060000;
61 | public static final int can_not_edit=0x7f060014;
62 | public static final int cancel=0x7f060002;
63 | public static final int crash_message=0x7f060017;
64 | public static final int error=0x7f060003;
65 | public static final int failure=0x7f060005;
66 | public static final int no_strings_for_editing=0x7f060013;
67 | public static final int ok=0x7f060001;
68 | public static final int out_of_memory=0x7f060016;
69 | public static final int parsing=0x7f060006;
70 | public static final int saving=0x7f060015;
71 | public static final int search=0x7f06000e;
72 | public static final int search_hint=0x7f060011;
73 | public static final int search_result=0x7f060010;
74 | public static final int searching=0x7f06000f;
75 | public static final int skip_already_translated=0x7f06000d;
76 | public static final int source_language=0x7f06000b;
77 | public static final int string_edit=0x7f060012;
78 | public static final int success=0x7f060004;
79 | public static final int target_language=0x7f06000c;
80 | public static final int translate=0x7f060007;
81 | public static final int translate_success=0x7f06000a;
82 | public static final int translating=0x7f060009;
83 | public static final int translator=0x7f060008;
84 | }
85 | public static final class style {
86 | public static final int AppTheme=0x7f070000;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResFlagsAttr.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 | import java.util.Arrays;
21 | import java.util.Comparator;
22 |
23 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
24 | import zhao.arsceditor.ResDecoder.IO.Duo;
25 | import zhao.arsceditor.ResDecoder.data.ResResource;
26 |
27 | /**
28 | * @author Ryszard Wiśniewski
29 | */
30 | public class ResFlagsAttr extends ResAttr {
31 | private static class FlagItem {
32 | public final int flag;
33 | public String value;
34 |
35 | public FlagItem(ResReferenceValue ref, int flag) {
36 | this.flag = flag;
37 | }
38 |
39 | public String getValue() throws IOException {
40 | if (value == null) {
41 | // value = ref.getReferent().getName();
42 | }
43 | return value;
44 | }
45 | }
46 |
47 | private FlagItem[] mFlags;
48 |
49 | private final FlagItem[] mItems;
50 |
51 | private FlagItem[] mZeroFlags;
52 |
53 | ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n,
54 | Duo[] items) {
55 | super(parent, type, min, max, l10n);
56 |
57 | mItems = new FlagItem[items.length];
58 | for (int i = 0; i < items.length; i++) {
59 | mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
60 | }
61 | }
62 |
63 | @Override
64 | public String convertToResXmlFormat(ResScalarValue value) throws IOException {
65 | if (value instanceof ResReferenceValue) {
66 | return value.encodeAsResValue();
67 | }
68 | if (!(value instanceof ResIntValue)) {
69 | return super.convertToResXmlFormat(value);
70 | }
71 | loadFlags();
72 | int intVal = ((ResIntValue) value).getValue();
73 |
74 | if (intVal == 0) {
75 | return renderFlags(mZeroFlags);
76 | }
77 |
78 | FlagItem[] flagItems = new FlagItem[mFlags.length];
79 | int[] flags = new int[mFlags.length];
80 | int flagsCount = 0;
81 | for (int i = 0; i < mFlags.length; i++) {
82 | FlagItem flagItem = mFlags[i];
83 | int flag = flagItem.flag;
84 |
85 | if ((intVal & flag) != flag) {
86 | continue;
87 | }
88 |
89 | if (!isSubpartOf(flag, flags)) {
90 | flags[flagsCount] = flag;
91 | flagItems[flagsCount++] = flagItem;
92 | }
93 | }
94 | return renderFlags(Arrays.copyOf(flagItems, flagsCount));
95 | }
96 |
97 | private boolean isSubpartOf(int flag, int[] flags) {
98 | for (int i = 0; i < flags.length; i++) {
99 | if ((flags[i] & flag) == flag) {
100 | return true;
101 | }
102 | }
103 | return false;
104 | }
105 |
106 | private void loadFlags() {
107 | if (mFlags != null) {
108 | return;
109 | }
110 |
111 | FlagItem[] zeroFlags = new FlagItem[mItems.length];
112 | int zeroFlagsCount = 0;
113 | FlagItem[] flags = new FlagItem[mItems.length];
114 | int flagsCount = 0;
115 |
116 | for (int i = 0; i < mItems.length; i++) {
117 | FlagItem item = mItems[i];
118 | if (item.flag == 0) {
119 | zeroFlags[zeroFlagsCount++] = item;
120 | } else {
121 | flags[flagsCount++] = item;
122 | }
123 | }
124 |
125 | mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
126 | mFlags = Arrays.copyOf(flags, flagsCount);
127 |
128 | Arrays.sort(mFlags, new Comparator() {
129 | @Override
130 | public int compare(FlagItem o1, FlagItem o2) {
131 | return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(Integer.bitCount(o1.flag));
132 | }
133 | });
134 | }
135 |
136 | private String renderFlags(FlagItem[] flags) throws IOException {
137 | String ret = "";
138 | for (int i = 0; i < flags.length; i++) {
139 | ret += "|" + flags[i].getValue();
140 | }
141 | if (ret.equals("")) {
142 | return ret;
143 | }
144 | return ret.substring(1);
145 | }
146 |
147 | @Override
148 | protected void serializeBody(ARSCCallBack back, ResResource res) throws IOException, IOException {
149 | for (int i = 0; i < mItems.length; i++) {
150 | FlagItem item = mItems[i];
151 | back.back(res.getConfig().toString(), res.getResSpec().getType().getName(), item.getValue(),
152 | String.format("0x%08x", item.flag));
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResTable.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.HashMap;
21 | import java.util.LinkedHashMap;
22 | import java.util.LinkedHashSet;
23 | import java.util.Map;
24 | import java.util.Set;
25 |
26 | import android.annotation.SuppressLint;
27 | import zhao.arsceditor.AndrolibResources;
28 |
29 | /**
30 | * @author Ryszard Wiśniewski
31 | */
32 | public class ResTable {
33 | private boolean mAnalysisMode = false;
34 |
35 | private final Set mFramePackages = new LinkedHashSet();
36 | private final Set mMainPackages = new LinkedHashSet();
37 | private int mPackageId;
38 |
39 | private String mPackageOriginal;
40 | private String mPackageRenamed;
41 | @SuppressLint("UseSparseArrays")
42 | private final Map mPackagesById = new HashMap();
43 | private final Map mPackagesByName = new HashMap();
44 | private Map mSdkInfo = new LinkedHashMap();
45 |
46 | private boolean mSharedLibrary = false;
47 | private Map mVersionInfo = new LinkedHashMap();
48 |
49 | public ResTable() {
50 | }
51 |
52 | public ResTable(AndrolibResources andRes) {
53 | }
54 |
55 | public void addPackage(ResPackage pkg, boolean main) throws IOException {
56 | Integer id = pkg.getId();
57 | if (mPackagesById.containsKey(id)) {
58 | throw new IOException("Multiple packages: id=" + id.toString());
59 | }
60 | String name = pkg.getName();
61 | if (mPackagesByName.containsKey(name)) {
62 | throw new IOException("Multiple packages: name=" + name);
63 | }
64 |
65 | mPackagesById.put(id, pkg);
66 | mPackagesByName.put(name, pkg);
67 | if (main) {
68 | mMainPackages.add(pkg);
69 | } else {
70 | mFramePackages.add(pkg);
71 | }
72 | }
73 |
74 | public void addSdkInfo(String key, String value) {
75 | mSdkInfo.put(key, value);
76 | }
77 |
78 | public void addVersionInfo(String key, String value) {
79 | mVersionInfo.put(key, value);
80 | }
81 |
82 | public void clearSdkInfo() {
83 | mSdkInfo.clear();
84 | }
85 |
86 | public boolean getAnalysisMode() {
87 | return mAnalysisMode;
88 | }
89 |
90 | public ResPackage getPackage(int id) throws IOException {
91 | ResPackage pkg = mPackagesById.get(id);
92 | if (pkg != null) {
93 | return pkg;
94 | }
95 | /*
96 | * if (mAndRes != null) { return mAndRes.loadFrameworkPkg(this, id,
97 | * mAndRes.apkOptions.frameworkTag); }
98 | */
99 | throw new IOException(String.format("package: id=%d", id));
100 | }
101 |
102 | public int getPackageId() {
103 | return mPackageId;
104 | }
105 |
106 | public String getPackageOriginal() {
107 | return mPackageOriginal;
108 | }
109 |
110 | public String getPackageRenamed() {
111 | return mPackageRenamed;
112 | }
113 |
114 | public ResResSpec getResSpec(int resID) throws IOException {
115 | // The pkgId is 0x00. That means a shared library is using its
116 | // own resource, so lie to the caller replacing with its own
117 | // packageId
118 | if (resID >> 24 == 0) {
119 | int pkgId = (mPackageId == 0 ? 2 : mPackageId);
120 | resID = (0xFF000000 & (pkgId << 24)) | resID;
121 | }
122 | return getResSpec(new ResID(resID));
123 | }
124 |
125 | public ResResSpec getResSpec(ResID resID) throws IOException {
126 | return getPackage(resID.package_).getResSpec(resID);
127 | }
128 |
129 | public Map getSdkInfo() {
130 | return mSdkInfo;
131 | }
132 |
133 | public boolean getSharedLibrary() {
134 | return mSharedLibrary;
135 | }
136 |
137 | public Map getVersionInfo() {
138 | return mVersionInfo;
139 | }
140 |
141 | public boolean hasPackage(int id) {
142 | return mPackagesById.containsKey(id);
143 | }
144 |
145 | public boolean hasPackage(String name) {
146 | return mPackagesByName.containsKey(name);
147 | }
148 |
149 | public Set listFramePackages() {
150 | return mFramePackages;
151 | }
152 |
153 | public Set listMainPackages() {
154 | return mMainPackages;
155 | }
156 |
157 | public void setAnalysisMode(boolean mode) {
158 | mAnalysisMode = mode;
159 | }
160 |
161 | public void setPackageId(int id) {
162 | mPackageId = id;
163 | }
164 |
165 | public void setPackageOriginal(String pkg) {
166 | mPackageOriginal = pkg;
167 | }
168 |
169 | public void setPackageRenamed(String pkg) {
170 | mPackageRenamed = pkg;
171 | }
172 |
173 | public void setSharedLibrary(boolean flag) {
174 | mSharedLibrary = flag;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/value/ResAttr.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data.value;
18 |
19 | import java.io.IOException;
20 |
21 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
22 | import zhao.arsceditor.ResDecoder.GetResValues;
23 | import zhao.arsceditor.ResDecoder.IO.Duo;
24 | import zhao.arsceditor.ResDecoder.data.ResPackage;
25 | import zhao.arsceditor.ResDecoder.data.ResResource;
26 |
27 | /**
28 | * @author Ryszard Wiśniewski
29 | */
30 | public class ResAttr extends ResBagValue implements GetResValues {
31 | private static final int BAG_KEY_ATTR_L10N = 0x01000003;
32 |
33 | private static final int BAG_KEY_ATTR_MAX = 0x01000002;
34 |
35 | private static final int BAG_KEY_ATTR_MIN = 0x01000001;
36 |
37 | public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
38 |
39 | @SuppressWarnings("unused")
40 | private final static int TYPE_ANY_STRING = 0xee;
41 |
42 | private final static int TYPE_BOOL = 0x08;
43 |
44 | private final static int TYPE_COLOR = 0x10;
45 | private final static int TYPE_DIMEN = 0x40;
46 | private static final int TYPE_ENUM = 0x00010000;
47 | private static final int TYPE_FLAGS = 0x00020000;
48 |
49 | private final static int TYPE_FLOAT = 0x20;
50 | private final static int TYPE_FRACTION = 0x80;
51 | private final static int TYPE_INT = 0x04;
52 | private final static int TYPE_REFERENCE = 0x01;
53 |
54 | private final static int TYPE_STRING = 0x02;
55 |
56 | @SuppressWarnings("unchecked")
57 | public static ResAttr factory(ResReferenceValue parent, Duo[] items,
58 | ResValueFactory factory, ResPackage pkg) throws IOException {
59 |
60 | int type = ((ResIntValue) items[0].m2).getValue();
61 | int scalarType = type & 0xffff;
62 | Integer min = null, max = null;
63 | Boolean l10n = null;
64 | int i;
65 | for (i = 1; i < items.length; i++) {
66 | switch (items[i].m1) {
67 | case BAG_KEY_ATTR_MIN:
68 | min = ((ResIntValue) items[i].m2).getValue();
69 | continue;
70 | case BAG_KEY_ATTR_MAX:
71 | max = ((ResIntValue) items[i].m2).getValue();
72 | continue;
73 | case BAG_KEY_ATTR_L10N:
74 | l10n = ((ResIntValue) items[i].m2).getValue() != 0;
75 | continue;
76 | }
77 | break;
78 | }
79 |
80 | if (i == items.length) {
81 | return new ResAttr(parent, scalarType, min, max, l10n);
82 | }
83 | Duo[] attrItems = new Duo[items.length - i];
84 | int j = 0;
85 | for (; i < items.length; i++) {
86 | int resId = items[i].m1;
87 | pkg.addSynthesizedRes(resId);
88 | attrItems[j++] = new Duo(factory.newReference(resId, null),
89 | (ResIntValue) items[i].m2);
90 | }
91 | switch (type & 0xff0000) {
92 | case TYPE_ENUM:
93 | return new ResEnumAttr(parent, scalarType, min, max, l10n, attrItems);
94 | case TYPE_FLAGS:
95 | return new ResFlagsAttr(parent, scalarType, min, max, l10n, attrItems);
96 | }
97 |
98 | throw new IOException("Could not decode attr value");
99 | }
100 |
101 | @SuppressWarnings("unused")
102 | private final Boolean mL10n;
103 | @SuppressWarnings("unused")
104 | private final Integer mMax;
105 | @SuppressWarnings("unused")
106 | private final Integer mMin;
107 | private final int mType;
108 |
109 | ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max, Boolean l10n) {
110 | super(parentVal);
111 | mType = type;
112 | mMin = min;
113 | mMax = max;
114 | mL10n = l10n;
115 | }
116 |
117 | public String convertToResXmlFormat(ResScalarValue value) throws IOException {
118 | return value.encodeAsResValue();
119 | }
120 |
121 | @Override
122 | public void getResValues(ARSCCallBack back, ResResource res) throws IOException {
123 | serializeBody(back, res);
124 | }
125 |
126 | protected String getTypeAsString() {
127 | String s = "";
128 | if ((mType & TYPE_REFERENCE) != 0) {
129 | s += "|reference";
130 | }
131 | if ((mType & TYPE_STRING) != 0) {
132 | s += "|string";
133 | }
134 | if ((mType & TYPE_INT) != 0) {
135 | s += "|integer";
136 | }
137 | if ((mType & TYPE_BOOL) != 0) {
138 | s += "|boolean";
139 | }
140 | if ((mType & TYPE_COLOR) != 0) {
141 | s += "|color";
142 | }
143 | if ((mType & TYPE_FLOAT) != 0) {
144 | s += "|float";
145 | }
146 | if ((mType & TYPE_DIMEN) != 0) {
147 | s += "|dimension";
148 | }
149 | if ((mType & TYPE_FRACTION) != 0) {
150 | s += "|fraction";
151 | }
152 | if (s.isEmpty()) {
153 | return null;
154 | }
155 | return s.substring(1);
156 | }
157 |
158 | protected void serializeBody(ARSCCallBack back, ResResource res) throws IOException, IOException {
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/AndrolibResources.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Ryszard Wiśniewski
3 | * Modified Copyright 2015 ZhaoHai <2801045898@qq.com>
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package zhao.arsceditor;
19 |
20 | import java.io.BufferedInputStream;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 |
24 | import android.annotation.SuppressLint;
25 | import android.content.Context;
26 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
27 | import zhao.arsceditor.ResDecoder.ARSCDecoder;
28 | import zhao.arsceditor.ResDecoder.AXMLDecoder;
29 | import zhao.arsceditor.ResDecoder.GetResValues;
30 | import zhao.arsceditor.ResDecoder.data.ResPackage;
31 | import zhao.arsceditor.ResDecoder.data.ResResource;
32 | import zhao.arsceditor.ResDecoder.data.ResTable;
33 | import zhao.arsceditor.ResDecoder.data.ResValuesFile;
34 |
35 | final public class AndrolibResources {
36 |
37 | // TODO: dirty static hack. I have to refactor decoding mechanisms.
38 | public static boolean sKeepBroken = true;
39 |
40 |
41 |
42 |
43 |
44 | private Context context;
45 |
46 | // ARSC解析器
47 | public ARSCDecoder mARSCDecoder;
48 |
49 | // AXML解析器
50 | public AXMLDecoder mAXMLDecoder;
51 |
52 | // ELF解析器
53 | //public Elf elfParser;
54 |
55 | private ResPackage pkg;
56 |
57 | public AndrolibResources(Context context) {
58 | this.context = context;
59 | }
60 |
61 | // 解析ARSC的方法
62 | public void decodeARSC(ResTable resTable, ARSCCallBack callback) throws IOException {
63 |
64 | for (ResPackage pkg : resTable.listMainPackages()) {
65 |
66 | System.out.println("Decoding values */* XMLs...");
67 | for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
68 | generateValuesFile(valuesFile, callback);
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * 解析AXML的方法
75 | *
76 | * @param AXMLStream
77 | * @return 返回一个解析后的临时文件对象
78 | * @throws IOException
79 | */
80 | public void decodeAXML(InputStream AXMLStream) throws IOException {
81 | mAXMLDecoder = AXMLDecoder.read(AXMLStream);
82 | }
83 |
84 | @SuppressLint("NewApi")
85 | private void generateValuesFile(ResValuesFile valuesFile, ARSCCallBack callback) throws IOException {
86 | for (ResResource res : valuesFile.listResources()) {
87 | if (valuesFile.isSynthesized(res)) {
88 | continue;
89 | }
90 | ((GetResValues) res.getValue()).getResValues(callback, res);
91 | }
92 | }
93 |
94 | public ResPackage getFramPackage() {
95 | return pkg;
96 | }
97 |
98 | private ResPackage[] getResPackagesFromARSC(ARSCDecoder decoder, InputStream ARSCStream, ResTable resTable,
99 | boolean keepBroken) throws IOException {
100 | return decoder.decode(decoder, new BufferedInputStream(ARSCStream), false, keepBroken, resTable).getPackages();
101 | }
102 |
103 | public ResTable getResTable() {
104 | ResTable resTable = new ResTable(this);
105 | return resTable;
106 | }
107 |
108 | public ResTable getResTable(InputStream ARSCStream) throws IOException {
109 | return getResTable(ARSCStream, true);
110 | }
111 |
112 | public ResTable getResTable(InputStream ARSCStream, boolean loadMainPkg) throws IOException {
113 | ResTable resTable = new ResTable(this);
114 | if (loadMainPkg) {
115 | loadMainPkg(resTable, ARSCStream);
116 | }
117 | return resTable;
118 | }
119 |
120 | public ResPackage loadMainPkg(ResTable resTable, InputStream ARSCStream) throws IOException {
121 | System.out.println("Loading resource table...");
122 | mARSCDecoder = new ARSCDecoder(new BufferedInputStream(ARSCStream), resTable, sKeepBroken);
123 | ResPackage[] pkgs = getResPackagesFromARSC(mARSCDecoder, ARSCStream, resTable, sKeepBroken);
124 | ResPackage pkg = null;
125 |
126 | switch (pkgs.length) {
127 | case 1:
128 | pkg = pkgs[0];
129 | break;
130 | case 2:
131 | if (pkgs[0].getName().equals("android")) {
132 | System.out.println("Skipping \"android\" package group");
133 | pkg = pkgs[1];
134 | } else if (pkgs[0].getName().equals("com.htc")) {
135 | System.out.println("Skipping \"htc\" package group");
136 | pkg = pkgs[1];
137 | }
138 | break;
139 | default:
140 | pkg = selectPkgWithMostResSpecs(pkgs);
141 | }
142 |
143 | if (pkg == null) {
144 | throw new IOException("Arsc files with zero or multiple packages");
145 | }
146 |
147 | resTable.addPackage(pkg, true);
148 | System.out.println("Loaded.");
149 | return pkg;
150 | }
151 |
152 | public void pushFramResPackage(ResPackage pkg) {
153 | this.pkg = pkg;
154 | }
155 |
156 | public ResPackage selectPkgWithMostResSpecs(ResPackage[] pkgs) throws IOException {
157 | int id = 0;
158 | int value = 0;
159 |
160 | for (ResPackage resPackage : pkgs) {
161 | if (resPackage.getResSpecCount() > value && !resPackage.getName().equalsIgnoreCase("android")) {
162 | value = resPackage.getResSpecCount();
163 | id = resPackage.getId();
164 | }
165 | }
166 |
167 | // if id is still 0, we only have one pkgId which is "android" -> 1
168 | return (id == 0) ? pkgs[0] : pkgs[1];
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResPackage.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.io.IOException;
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 | import java.util.HashMap;
23 | import java.util.HashSet;
24 | import java.util.LinkedHashMap;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Set;
28 | import java.util.logging.Logger;
29 |
30 | import zhao.arsceditor.ResDecoder.GetResValues;
31 | import zhao.arsceditor.ResDecoder.IO.Duo;
32 | import zhao.arsceditor.ResDecoder.data.value.ResFileValue;
33 | import zhao.arsceditor.ResDecoder.data.value.ResValueFactory;
34 |
35 | /**
36 | * @author Ryszard Wiśniewski
37 | */
38 | public class ResPackage {
39 | private final static Logger LOGGER = Logger.getLogger(ResPackage.class.getName());
40 | private final Map mConfigs = new LinkedHashMap();
41 | private final int mId;
42 | private final String mName;
43 | private final Map mResSpecs = new LinkedHashMap();
44 | private final ResTable mResTable;
45 | private final Set mSynthesizedRes = new HashSet();
46 |
47 | private final Map mTypes = new LinkedHashMap();
48 |
49 | private ResValueFactory mValueFactory;
50 |
51 | public ResPackage(ResTable resTable, int id, String name) {
52 | this.mResTable = resTable;
53 | this.mId = id;
54 | this.mName = name;
55 | }
56 |
57 | public void addConfig(ResType config) throws IOException {
58 | if (mConfigs.put(config.getFlags(), config) != null) {
59 | // throw new IOException("Multiple configs: " + config);
60 | }
61 | }
62 |
63 | public void addResource(ResResource res) {
64 | }
65 |
66 | public void addResSpec(ResResSpec spec) throws IOException {
67 | if (mResSpecs.put(spec.getId(), spec) != null) {
68 | // throw new IOException("Multiple resource specs: " + spec);
69 | }
70 | }
71 |
72 | public void addSynthesizedRes(int resId) {
73 | mSynthesizedRes.add(new ResID(resId));
74 | }
75 |
76 | public void addType(ResTypeSpec type) throws IOException {
77 | if (mTypes.containsKey(type.getName())) {
78 | LOGGER.warning("Multiple types detected! " + type + " ignored!");
79 | } else {
80 | mTypes.put(type.getName(), type);
81 | }
82 | }
83 |
84 | @Override
85 | public boolean equals(Object obj) {
86 | if (obj == null) {
87 | return false;
88 | }
89 | if (getClass() != obj.getClass()) {
90 | return false;
91 | }
92 | final ResPackage other = (ResPackage) obj;
93 | if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) {
94 | return false;
95 | }
96 | if (this.mId != other.mId) {
97 | return false;
98 | }
99 | return true;
100 | }
101 |
102 | public ResType getConfig(ResConfigFlags flags) throws IOException {
103 | ResType config = mConfigs.get(flags);
104 | if (config == null) {
105 | // throw new IOException("config: " + flags);
106 | }
107 | return config;
108 | }
109 |
110 | public List getConfigs() {
111 | return new ArrayList(mConfigs.values());
112 | }
113 |
114 | public int getId() {
115 | return mId;
116 | }
117 |
118 | public String getName() {
119 | return mName;
120 | }
121 |
122 | public ResType getOrCreateConfig(ResConfigFlags flags) throws IOException {
123 | ResType config = mConfigs.get(flags);
124 | if (config == null) {
125 | config = new ResType(flags);
126 | mConfigs.put(flags, config);
127 | }
128 | return config;
129 | }
130 |
131 | public ResResSpec getResSpec(ResID resID) throws IOException {
132 | ResResSpec spec = mResSpecs.get(resID);
133 | if (spec == null) {
134 | // throw new IOException("resource spec: " + resID.toString());
135 | }
136 | return spec;
137 | }
138 |
139 | public int getResSpecCount() {
140 | return mResSpecs.size();
141 | }
142 |
143 | public ResTable getResTable() {
144 | return mResTable;
145 | }
146 |
147 | public ResTypeSpec getType(String typeName) throws IOException {
148 | ResTypeSpec type = mTypes.get(typeName);
149 | if (type == null) {
150 | // throw new IOException("type: " + typeName);
151 | }
152 | return type;
153 | }
154 |
155 | public ResValueFactory getValueFactory() {
156 | if (mValueFactory == null) {
157 | mValueFactory = new ResValueFactory(this);
158 | }
159 | return mValueFactory;
160 | }
161 |
162 | public boolean hasConfig(ResConfigFlags flags) {
163 | return mConfigs.containsKey(flags);
164 | }
165 |
166 | @Override
167 | public int hashCode() {
168 | int hash = 17;
169 | hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0);
170 | hash = 31 * hash + this.mId;
171 | return hash;
172 | }
173 |
174 | public boolean hasResSpec(ResID resID) {
175 | return mResSpecs.containsKey(resID);
176 | }
177 |
178 | public boolean hasType(String typeName) {
179 | return mTypes.containsKey(typeName);
180 | }
181 |
182 | boolean isSynthesized(ResID resId) {
183 | return mSynthesizedRes.contains(resId);
184 | }
185 |
186 | public Set listFiles() {
187 | Set ret = new HashSet();
188 | for (ResResSpec spec : mResSpecs.values()) {
189 | for (ResResource res : spec.listResources()) {
190 | if (res.getValue() instanceof ResFileValue) {
191 | ret.add(res);
192 | }
193 | }
194 | }
195 | return ret;
196 | }
197 |
198 | public List listResSpecs() {
199 | return new ArrayList(mResSpecs.values());
200 | }
201 |
202 | public List listTypes() {
203 | return new ArrayList(mTypes.values());
204 | }
205 |
206 | public Collection listValuesFiles() {
207 | Map, ResValuesFile> ret = new HashMap, ResValuesFile>();
208 | for (ResResSpec spec : mResSpecs.values()) {
209 | for (ResResource res : spec.listResources()) {
210 | if (res.getValue() instanceof GetResValues) {
211 | ResTypeSpec type = res.getResSpec().getType();
212 | ResType config = res.getConfig();
213 | Duo key = new Duo(type, config);
214 | ResValuesFile values = ret.get(key);
215 | if (values == null) {
216 | values = new ResValuesFile(this, type, config);
217 | ret.put(key, values);
218 | }
219 | values.addResource(res);
220 | }
221 | }
222 | }
223 | return ret.values();
224 | }
225 |
226 | public void removeResource(ResResource res) {
227 | }
228 |
229 | public void removeResSpec(ResResSpec spec) throws IOException {
230 | mResSpecs.remove(spec.getId());
231 | }
232 |
233 | @Override
234 | public String toString() {
235 | return mName;
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/IO/LEDataInputStream.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * 二进制文件数据读取工具
18 | * @author zhaohai
19 | * @time 2015/10/10
20 | * */
21 | package zhao.arsceditor.ResDecoder.IO;
22 |
23 | import java.io.ByteArrayInputStream;
24 | import java.io.ByteArrayOutputStream;
25 | import java.io.DataInputStream;
26 | import java.io.IOException;
27 | import java.io.InputStream;
28 | import java.lang.reflect.Field;
29 |
30 | public class LEDataInputStream {
31 | /** 二进制输入流 */
32 | private DataInputStream dis;
33 | /** 输入流 */
34 | private InputStream is;
35 |
36 | private boolean mIsLittleEndian = true;
37 |
38 | /**
39 | * 缓冲字节数组区
40 | */
41 | protected byte[] work;
42 |
43 | public long size;
44 |
45 | public LEDataInputStream(byte[] data) throws IOException {
46 | this(new ByteArrayInputStream(data));
47 | }
48 |
49 | /**
50 | * 构造函数
51 | *
52 | * @throws IOException
53 | */
54 | public LEDataInputStream(InputStream in) throws IOException {
55 | // 获取输入流
56 | this.is = in;
57 | this.dis = new DataInputStream(is);
58 | work = new byte[8];
59 | size = dis.available();
60 | }
61 |
62 | /**
63 | * 剩余未读取的数据大小
64 | */
65 | public int available() throws IOException {
66 | return is.available();
67 | }
68 |
69 | /**
70 | * 关闭流
71 | */
72 | public void close() throws IOException {
73 | dis.close();
74 | is.close();
75 | }
76 |
77 | public void mark(int readlimit) throws IOException {
78 | is.mark(readlimit);
79 | }
80 |
81 | /**
82 | * 读取一些数据,储存到buffer中
83 | *
84 | * @throws IOException
85 | */
86 | public int read(byte[] buffer, int start, int end) throws IOException {
87 | return dis.read(buffer, start, end);
88 | }
89 |
90 | /**
91 | * 读取一个字节
92 | *
93 | * @throws IOException
94 | */
95 | public byte readByte() throws IOException {
96 | return dis.readByte();
97 | }
98 |
99 | /**
100 | * 读取字节,储存到数组中,直到数组被填满
101 | *
102 | * @throws IOException
103 | */
104 | public void readFully(byte ba[]) throws IOException {
105 | dis.readFully(ba, 0, ba.length);
106 | }
107 |
108 | /**
109 | * 读取字节,储存到数组offset开始,len长度大小中,直到数组被填满
110 | *
111 | * @throws IOException
112 | */
113 | public void readFully(byte ba[], int off, int len) throws IOException {
114 | dis.readFully(ba, off, len);
115 | }
116 |
117 | /**
118 | * 读取一个32位的int型数据
119 | *
120 | * @throws IOException
121 | */
122 | public int readInt() throws IOException {
123 | if (mIsLittleEndian) {
124 | dis.readFully(work, 0, 4);
125 | return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8 | (work[0] & 0xff);
126 | } else {
127 | return dis.readInt();
128 | }
129 | }
130 |
131 | /**
132 | * 读取32位int数据,并储存到数组中,直到数组填满
133 | *
134 | * @throws IOException
135 | */
136 | public int[] readIntArray(int length) throws IOException {
137 | int[] array = new int[length];
138 | for (int i = 0; i < length; i++) {
139 | array[i] = readInt();
140 | }
141 | return array;
142 | }
143 |
144 | /***
145 | * 读取一个64位长整型数据
146 | */
147 | public final long readLong() throws IOException {
148 | if (mIsLittleEndian) {
149 | dis.readFully(work, 0, 8);
150 | return (long) (work[7]) << 56 | (long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40
151 | | (long) (work[4] & 0xff) << 32 | (long) (work[3] & 0xff) << 24 | (long) (work[2] & 0xff) << 16
152 | | (long) (work[1] & 0xff) << 8 | work[0] & 0xff;
153 | } else {
154 | return dis.readLong();
155 | }
156 | }
157 |
158 | /**
159 | * 读取包名
160 | */
161 | public String readNulEndedString(int length, boolean fixed) throws IOException {
162 | StringBuilder string = new StringBuilder(16);
163 | while (length-- != 0) {
164 | short ch = readShort();
165 | if (ch == 0) {
166 | break;
167 | }
168 | string.append((char) ch);
169 | }
170 | if (fixed) {
171 | skipBytes(length * 2);
172 | }
173 |
174 | return string.toString();
175 | }
176 |
177 | /**
178 | * 读取一个16位的short型数据
179 | *
180 | * @throws IOException
181 | */
182 | public short readShort() throws IOException {
183 | if (mIsLittleEndian) {
184 | dis.readFully(work, 0, 2);
185 | return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
186 | } else {
187 | return dis.readShort();
188 | }
189 | }
190 |
191 | /***
192 | * 读取一个16位的无符号Short型数据
193 | */
194 | public int readUnsignedShort() throws IOException {
195 | return dis.readUnsignedShort();
196 | }
197 |
198 | public void reset() throws IOException {
199 | is.reset();
200 | }
201 |
202 | /**
203 | * 跳转
204 | *
205 | * @throws IllegalArgumentException
206 | * @throws IllegalAccessException
207 | * @throws IOException
208 | * @throws NoSuchFieldException
209 | */
210 | public void seek(long position) throws IOException {
211 | if (is instanceof ByteArrayInputStream) {
212 | Class clazz = ByteArrayInputStream.class;
213 | Field field;
214 | try {
215 | field = clazz.getDeclaredField("pos");
216 | field.setAccessible(true);
217 | field.setInt(is, (int) position);
218 | } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
219 | e.printStackTrace();
220 | throw new IOException("Unsupported");
221 | }
222 | } else {
223 | throw new IOException("Unsupported");
224 | }
225 | }
226 |
227 | public void setIsLittleEndian(boolean isLittleEndian) {
228 | mIsLittleEndian = isLittleEndian;
229 | }
230 |
231 | /**
232 | * 跳过1个字节
233 | *
234 | * @throws IOException
235 | */
236 | public void skipByte() throws IOException {
237 | skipBytes(1);
238 | }
239 |
240 | /**
241 | * 跳过n个字节
242 | *
243 | * @throws IOException
244 | */
245 | public void skipBytes(int n) throws IOException {
246 | dis.skipBytes(n);
247 | }
248 |
249 | public void skipCheckByte(byte expected) throws IOException {
250 | byte got = readByte();
251 | if (got != expected) {
252 | throw new IOException(String.format("CheckByte Expected: 0x%08x, got: 0x%08x", expected, got));
253 | }
254 | }
255 |
256 | public int skipCheckChunkTypeInt(int expected, int possible) throws IOException {
257 | int got = readInt();
258 |
259 | if (got == possible) {
260 | skipCheckChunkTypeInt(expected, -1);
261 | } else if (got != expected) {
262 | throw new IOException(String.format("CheckChunkTypeInt Expected: 0x%08x, got: 0x%08x", expected, got));
263 | }
264 | return got;
265 | }
266 |
267 | public void skipCheckInt(int expected) throws IOException {
268 | int got = readInt();
269 | if (got != expected) {
270 | throw new IOException(String.format("CheckInt Expected: 0x%08x, got: 0x%08x", expected, got));
271 | }
272 | }
273 |
274 | public void skipCheckShort(short expected) throws IOException {
275 | short got = readShort();
276 | if (got != expected) {
277 | throw new IOException(String.format("CheckShort Expected: 0x%08x, got: 0x%08x", expected, got));
278 | }
279 | }
280 |
281 | public static byte[] toByteArray(InputStream input) throws IOException {
282 | ByteArrayOutputStream output = new ByteArrayOutputStream();
283 | byte[] buffer = new byte[4096];
284 | int n = 0;
285 | while (-1 != (n = input.read(buffer))) {
286 | output.write(buffer, 0, n);
287 | }
288 | return output.toByteArray();
289 | }
290 |
291 | /**
292 | * 跳过一个32位的int型数据
293 | *
294 | * @throws IOException
295 | */
296 | public void skipInt() throws IOException {
297 | skipBytes(4);
298 | }
299 |
300 | /**
301 | * 跳过一个16位的short型数据
302 | *
303 | * @throws IOException
304 | */
305 | public void skipShort() throws IOException {
306 | skipBytes(2);
307 | }
308 |
309 | }
310 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/Translate/DoTranslate.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.Translate;
18 |
19 | import java.util.List;
20 |
21 | import com.memetix.mst.language.Language;
22 |
23 | import android.annotation.SuppressLint;
24 | import android.app.AlertDialog;
25 | import android.app.ProgressDialog;
26 | import android.content.Context;
27 | import android.content.DialogInterface;
28 | import android.os.AsyncTask;
29 | import android.view.LayoutInflater;
30 | import android.view.View;
31 | import android.widget.CheckBox;
32 | import android.widget.CompoundButton;
33 | import android.widget.CompoundButton.OnCheckedChangeListener;
34 | import android.widget.Spinner;
35 | import android.widget.Toast;
36 | import zhao.arsceditor.MainActivity;
37 | import zhao.arsceditor.R;
38 |
39 | @SuppressWarnings("deprecation")
40 | public class DoTranslate implements OnCheckedChangeListener {
41 | // 上下文
42 | private Context mContext;
43 | // 跳过已翻译的内容的CheckBox控件
44 | private CheckBox skip_already_translate;
45 | // 源语言,翻译为,翻译商选项的Spinner控件
46 | private Spinner src_type, translate_to, translator;
47 | // 能够翻译的语言的集合
48 | private String[] languages;
49 | // 必应的语言集合
50 | private Language languages_bing[] = { Language.AUTO_DETECT, Language.CHINESE_SIMPLIFIED, Language.ENGLISH,
51 | Language.JAPANESE, Language.KOREAN, Language.FRENCH, Language.THAI, Language.RUSSIAN, Language.GERMAN,
52 | Language.GREEK, Language.ITALIAN, Language.SPANISH, Language.PORTUGUESE, Language.ARABIC };
53 | // 跳过已翻译的内容的标志,默认为关闭
54 | private boolean skip_translate = false;
55 | // 用来储存需要翻译的内容
56 | private List source_list;
57 | // 用来储存翻译后的内容
58 | private List target_list;
59 | // 是否翻译全部,默认是
60 | private boolean translate_all = true;
61 | // 需要翻译的条目的位置
62 | private int position;
63 |
64 | // 构造函数
65 | public DoTranslate(List source_list, List target_list, boolean translate_all, Context context) {
66 | // 获取需要翻译的内容的集合
67 | this.source_list = source_list;
68 | // 获取目标集合
69 | this.target_list = target_list;
70 | this.translate_all = translate_all;
71 | // 承接上下文
72 | mContext = context;
73 | // 获取语言选项的数组
74 | languages = context.getResources().getStringArray(R.array.language_short);
75 | }
76 |
77 | // 初始化
78 | @SuppressLint("InflateParams")
79 | public void init(int _position) {
80 | // 获取需要翻译的条目所在位置
81 | position = _position;
82 |
83 | LayoutInflater factory = LayoutInflater.from(mContext);
84 | // 得到自定义对话框
85 | View DialogView = factory.inflate(R.layout.translate, null);
86 | // 创建对话框
87 | new AlertDialog.Builder(mContext).setView(DialogView)// 设置自定义对话框的样式
88 | .setTitle(R.string.translate) // 设置进度条对话框的标题
89 | .setNegativeButton(R.string.translate, new DialogInterface.OnClickListener() // 设置按钮,并监听
90 | {
91 | @SuppressWarnings("unchecked")
92 | @Override
93 | public void onClick(DialogInterface p1, int p2) {
94 | // 开启一个翻译线程
95 | new translate_task().execute(source_list, target_list);
96 | }
97 | }).create()// 创建
98 | .show(); // 显示对话框
99 |
100 | // 找到显示源语言的Spinner控件
101 | src_type = (Spinner) DialogView.findViewById(R.id.src_type);
102 | // 找到显示目标语言的Spinner控件
103 | translate_to = (Spinner) DialogView.findViewById(R.id.translate_to);
104 | // 找到翻译商的选项的Spinner控件
105 | translator = (Spinner) DialogView.findViewById(R.id.translator);
106 | // 找到显示跳过选项的CheckBox控件
107 | skip_already_translate = (CheckBox) DialogView.findViewById(R.id.skip_already_translate);
108 |
109 | // 源语言默认自动识别
110 | src_type.setSelection(0);
111 | // 翻译为默认选择中文
112 | translate_to.setSelection(1);
113 | // 默认选择百度翻译
114 | translator.setSelection(0);
115 | // 如果是翻译单个条目,需要隐藏“跳过已翻译的内容”的选项,否则需要显示该选项
116 | skip_already_translate.setVisibility(translate_all ? View.VISIBLE : View.GONE);
117 | skip_already_translate.setOnCheckedChangeListener(this);
118 | }
119 |
120 | // CheckBox是否被勾选的事件处理
121 | @Override
122 | public void onCheckedChanged(CompoundButton p1, boolean p2) {
123 | // 获取是否需要跳过已翻译的内容的选择
124 | skip_translate = p2;
125 | }
126 |
127 | // 翻译线程
128 | class translate_task extends AsyncTask
, Void, String> {
129 | // 翻译进度
130 | private String progress;
131 | // 进度条
132 | private ProgressDialog pdlg;
133 |
134 | @Override
135 | protected void onPreExecute() {
136 | // 创建一个进度条对话框
137 | pdlg = new ProgressDialog(mContext);
138 | // 设置标题
139 | pdlg.setTitle(R.string.translating);
140 | // 进度条对话框设置点击外面不消失
141 | pdlg.setCancelable(false);
142 | // 设置进度条对话框样式
143 | pdlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
144 | // 显示进度条对话框
145 | pdlg.show();
146 | }
147 |
148 | @Override
149 | protected String doInBackground(@SuppressWarnings("unchecked") List... parms) {
150 |
151 | // 翻译的结果
152 | String translatedTexte = null;
153 | try {
154 | switch (translator.getSelectedItemPosition()) {
155 | case 1:
156 | BingTranslate bingTranslator = new BingTranslate(languages_bing[src_type.getSelectedItemPosition()],
157 | languages_bing[translate_to.getSelectedItemPosition()]);
158 | if (translate_all) {
159 | for (String item : parms[0]) {
160 | if (!item.equals(""))
161 | // 获取必应翻译的结果
162 | translatedTexte = bingTranslator.getTranslateResult(item);
163 | else
164 | translatedTexte = item;
165 |
166 | if (skip_translate == true) {
167 | if (parms[1].get(parms[0].indexOf(item)).equals("")) {
168 | parms[1].remove(parms[0].indexOf(item));
169 | parms[1].add(parms[0].indexOf(item),
170 | translatedTexte.equals("") ? item : translatedTexte);
171 | }
172 | } else {
173 | parms[1].remove(parms[0].indexOf(item));
174 | parms[1].add(parms[0].indexOf(item),
175 | translatedTexte.equals("") ? item : translatedTexte);
176 | }
177 | progress = String.valueOf(parms[0].indexOf(item));
178 | publishProgress();
179 | }
180 | } else {
181 | if (!parms[0].get(position).equals(""))
182 | // 获取谷歌翻译的结果
183 | translatedTexte = bingTranslator.getTranslateResult(parms[0].get(position));
184 | else
185 | translatedTexte = parms[0].get(position);
186 | parms[1].remove(position);
187 | parms[1].add(position, translatedTexte.equals("") ? parms[0].get(position) : translatedTexte);
188 | }
189 | break;
190 | case 0:
191 | BaiduTranslate baiduTranslator = new BaiduTranslate(languages[src_type.getSelectedItemPosition()],
192 | languages[translate_to.getSelectedItemPosition()]);
193 | if (translate_all) {
194 | for (String item : parms[0]) {
195 | if (!item.equals(""))
196 | // 获取百度翻译的结果
197 | translatedTexte = baiduTranslator.getResult(item);
198 | else
199 | translatedTexte = item;
200 | if (skip_translate == true) {
201 | if (parms[1].get(parms[0].indexOf(item)).equals("")) {
202 | parms[1].remove(parms[0].indexOf(item));
203 | parms[1].add(parms[0].indexOf(item),
204 | translatedTexte.equals("") ? item : translatedTexte);
205 | }
206 | } else {
207 | parms[1].remove(parms[0].indexOf(item));
208 | parms[1].add(parms[0].indexOf(item),
209 | translatedTexte.equals("") ? item : translatedTexte);
210 | }
211 | progress = String.valueOf(parms[0].indexOf(item));
212 | publishProgress();
213 | }
214 | } else {
215 | if (!parms[0].get(position).equals(""))
216 | // 获取百度翻译的结果
217 | translatedTexte = baiduTranslator.getResult(parms[0].get(position));
218 | else
219 | translatedTexte = parms[0].get(position);
220 | parms[1].remove(position);
221 | parms[1].add(position, translatedTexte.equals("") ? parms[0].get(position) : translatedTexte);
222 | }
223 | break;
224 | }
225 | // 字符常量池已发生改变的标志
226 | ((MainActivity) mContext).isChanged = true;
227 | return mContext.getString(R.string.translate_success); // 返回翻译成功的结果
228 | } catch (Exception e) {
229 | for (String item : parms[1]) // 遍历获取listitem_change的内容
230 | {
231 | if (item != "") // 如果内容不为空
232 | {
233 | ((MainActivity) mContext).isChanged = true; // 字符已经改变的标志
234 | break; // 跳出循环
235 | }
236 | }
237 | return e.toString(); // 返回错误信息
238 | }
239 |
240 | }
241 |
242 | // 更新UI的方法
243 | @Override
244 | protected void onProgressUpdate(Void... p1) {
245 | pdlg.setMessage(progress + "/" + source_list.size());
246 | }
247 |
248 | // 线程执行完毕后的任务
249 | @Override
250 | protected void onPostExecute(String result) {
251 | super.onPostExecute(result);
252 | // 隐藏该进度条对话框
253 | pdlg.dismiss();
254 | // 更新字符常量池的显示
255 | ((MainActivity) mContext).mAdapter.notifyDataSetChanged();
256 | if (result == mContext.getString(R.string.translate_success))
257 | Toast.makeText(mContext, result, Toast.LENGTH_SHORT).show();
258 | else
259 | MainActivity.showMessage(mContext, result).show();// 显示错误信息
260 | }
261 | }
262 |
263 | }
264 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/CatchError/CrashHandler.java:
--------------------------------------------------------------------------------
1 | package zhao.arsceditor.CatchError;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.PrintWriter;
7 | import java.io.StringWriter;
8 | import java.io.Writer;
9 | import java.lang.Thread.UncaughtExceptionHandler;
10 | import java.lang.reflect.Field;
11 | import java.util.HashMap;
12 | import java.util.Map;
13 |
14 | import android.content.Context;
15 | import android.content.pm.PackageInfo;
16 | import android.content.pm.PackageManager;
17 | import android.content.pm.PackageManager.NameNotFoundException;
18 | import android.os.Build;
19 | import android.os.Environment;
20 | import android.os.Looper;
21 | import android.util.Log;
22 | import android.widget.Toast;
23 | import zhao.arsceditor.R;
24 |
25 | /**
26 | * ClassName: CrashHandler Function:
27 | * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
28 | *
29 | * @author Norris Norris.sly@gmail.com
30 | * @version
31 | * @since Ver 1.0 I used to be a programmer like you, then I took an arrow in
32 | * the knee
33 | * @see ──────────────────────────────────────────────────────────────────────────────────────────────────────
34 | * @Fields ──────────────────────────────────────────────────────────────────────────────────────────────────────
35 | */
36 | public class CrashHandler implements UncaughtExceptionHandler {
37 | /**
38 | * Log日志的tag String : TAG
39 | *
40 | * @since 2013-3-21下午8:44:28
41 | */
42 | private static final String TAG = "NorrisInfo";
43 | /**
44 | * 系统默认的UncaughtException处理类 Thread.UncaughtExceptionHandler :
45 | * mDefaultHandler
46 | *
47 | * @since 2013-3-21下午8:44:43
48 | */
49 | private Thread.UncaughtExceptionHandler mDefaultHandler;
50 | /**
51 | * CrashHandler实例 CrashHandler : mInstance
52 | *
53 | * @since 2013-3-21下午8:44:53
54 | */
55 | private static CrashHandler mInstance = new CrashHandler();
56 | /**
57 | * 程序的Context对象 Context : mContext
58 | *
59 | * @since 2013-3-21下午8:45:02
60 | */
61 | private Context mContext;
62 | /**
63 | * 用来存储设备信息和异常信息 Map : mLogInfo
64 | *
65 | * @since 2013-3-21下午8:46:15
66 | */
67 | private Map mLogInfo = new HashMap();
68 |
69 | /**
70 | * Creates a new instance of CrashHandler.
71 | */
72 | private CrashHandler() {
73 | }
74 |
75 | /**
76 | * getInstance:{获取CrashHandler实例 ,单例模式 } ──────────────────────────────────
77 | *
78 | * @return CrashHandler
79 | * @throws @since
80 | * I used to be a programmer like you, then I took an arrow in
81 | * the knee Ver 1.0
82 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
83 | * 2013-3-21下午8:52:24 Modified By Norris
84 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
85 | */
86 | public static CrashHandler getInstance() {
87 | return mInstance;
88 | }
89 |
90 | /**
91 | * init:{初始化} ──────────────────────────────────
92 | *
93 | * @param paramContext
94 | * @return void
95 | * @throws @since
96 | * I used to be a programmer like you, then I took an arrow in
97 | * the knee Ver 1.0
98 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
99 | * 2013-3-21下午8:52:45 Modified By Norris
100 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
101 | */
102 | public void init(Context paramContext) {
103 | mContext = paramContext;
104 | // 获取系统默认的UncaughtException处理器
105 | mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
106 | // 设置该CrashHandler为程序的默认处理器
107 | Thread.setDefaultUncaughtExceptionHandler(this);
108 | }
109 |
110 | /**
111 | * 当UncaughtException发生时会转入该重写的方法来处理 (non-Javadoc)
112 | *
113 | * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread,
114 | * java.lang.Throwable)
115 | */
116 | @SuppressWarnings("static-access")
117 | public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
118 | if (!handleException(paramThrowable) && mDefaultHandler != null) {
119 | // 如果自定义的没有处理则让系统默认的异常处理器来处理
120 | mDefaultHandler.uncaughtException(paramThread, paramThrowable);
121 | } else {
122 | try {
123 | // 如果处理了,让程序继续运行1秒再退出,保证文件保存并上传到服务器
124 | paramThread.sleep(2000);
125 | } catch (InterruptedException e) {
126 | e.printStackTrace();
127 | }
128 |
129 | // 退出程序
130 | android.os.Process.killProcess(android.os.Process.myPid());
131 | System.exit(1);
132 | }
133 | }
134 |
135 | /**
136 | * handleException:{自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.}
137 | * ──────────────────────────────────
138 | *
139 | * @param paramThrowable
140 | * @return true:如果处理了该异常信息;否则返回false.
141 | * @throws @since
142 | * I used to be a programmer like you, then I took an arrow in
143 | * the knee Ver 1.0
144 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
145 | * 2013-3-24下午12:28:53 Modified By Norris
146 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
147 | */
148 | public boolean handleException(Throwable paramThrowable) {
149 | if (paramThrowable == null)
150 | return false;
151 | new Thread() {
152 | public void run() {
153 | Looper.prepare();
154 | Toast.makeText(mContext, mContext.getString(R.string.crash_message), 0).show();
155 | Looper.loop();
156 | }
157 | }.start();
158 | // 获取设备参数信息
159 | getDeviceInfo(mContext);
160 | // 保存日志文件
161 | saveCrashLogToFile(paramThrowable);
162 | // 发送给作者
163 | /*
164 | * new Thread(new Runnable() { public void run() {
165 | *
166 | * new
167 | * sendreport().email("Debug报告",Environment.getExternalStorageDirectory(
168 | * ).getPath() + "/" + "SHC" + "/" +"Debug" + "/" + "CrashLog.log"); }
169 | *
170 | * }).start();
171 | */
172 | return true;
173 | }
174 |
175 | /**
176 | * getDeviceInfo:{获取设备参数信息} ──────────────────────────────────
177 | *
178 | * @param paramContext
179 | * @throws @since
180 | * I used to be a programmer like you, then I took an arrow in
181 | * the knee Ver 1.0
182 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
183 | * 2013-3-24下午12:30:02 Modified By Norris
184 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
185 | */
186 | public void getDeviceInfo(Context paramContext) {
187 | try {
188 | // 获得包管理器
189 | PackageManager mPackageManager = paramContext.getPackageManager();
190 | // 得到该应用的信息,即主Activity
191 | PackageInfo mPackageInfo = mPackageManager.getPackageInfo(paramContext.getPackageName(),
192 | PackageManager.GET_ACTIVITIES);
193 | if (mPackageInfo != null) {
194 | String versionName = mPackageInfo.versionName == null ? "null" : mPackageInfo.versionName;
195 | String versionCode = mPackageInfo.versionCode + "";
196 | mLogInfo.put("versionName", versionName);
197 | mLogInfo.put("versionCode", versionCode);
198 | }
199 | } catch (NameNotFoundException e) {
200 | e.printStackTrace();
201 | }
202 | // 反射机制
203 | Field[] mFields = Build.class.getDeclaredFields();
204 | // 迭代Build的字段key-value 此处的信息主要是为了在服务器端手机各种版本手机报错的原因
205 | for (Field field : mFields) {
206 | try {
207 | field.setAccessible(true);
208 | mLogInfo.put(field.getName(), field.get("").toString());
209 | Log.d(TAG, field.getName() + ":" + field.get(""));
210 | } catch (IllegalArgumentException e) {
211 | e.printStackTrace();
212 | } catch (IllegalAccessException e) {
213 | e.printStackTrace();
214 | }
215 | }
216 | }
217 |
218 | /**
219 | * saveCrashLogToFile:{将崩溃的Log保存到本地} TODO 可拓展,将Log上传至指定服务器路径
220 | * ──────────────────────────────────
221 | *
222 | * @param paramThrowable
223 | * @return FileName
224 | * @throws @since
225 | * I used to be a programmer like you, then I took an arrow in
226 | * the knee Ver 1.0
227 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
228 | * 2013-3-24下午12:31:01 Modified By Norris
229 | * ──────────────────────────────────────────────────────────────────────────────────────────────────────
230 | */
231 | private String saveCrashLogToFile(Throwable paramThrowable) {
232 | StringBuffer mStringBuffer = new StringBuffer();
233 | for (Map.Entry entry : mLogInfo.entrySet()) {
234 | String key = entry.getKey();
235 | String value = entry.getValue();
236 | mStringBuffer.append(key + "=" + value + "\r\n");
237 | }
238 | Writer mWriter = new StringWriter();
239 | PrintWriter mPrintWriter = new PrintWriter(mWriter);
240 | paramThrowable.printStackTrace(mPrintWriter);
241 | paramThrowable.printStackTrace();
242 | Throwable mThrowable = paramThrowable.getCause();
243 | // 迭代栈队列把所有的异常信息写入writer中
244 | while (mThrowable != null) {
245 | mThrowable.printStackTrace(mPrintWriter);
246 | // 换行 每个个异常栈之间换行
247 | mPrintWriter.append("\r\n");
248 | mThrowable = mThrowable.getCause();
249 | }
250 | // 记得关闭
251 | mPrintWriter.close();
252 | String mResult = mWriter.toString();
253 | mStringBuffer.append(mResult);
254 | // 保存文件,设置文件名
255 | String mFileName = "CrashLog.log";
256 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
257 | try {
258 | String mDirectory = Environment.getExternalStorageDirectory().getPath();
259 |
260 | FileOutputStream mFileOutputStream = new FileOutputStream(mDirectory + "/" + mFileName);
261 | mFileOutputStream.write(mStringBuffer.toString().getBytes());
262 | mFileOutputStream.close();
263 | return mFileName;
264 | } catch (FileNotFoundException e) {
265 | e.printStackTrace();
266 | } catch (IOException e) {
267 | e.printStackTrace();
268 | }
269 | }
270 | return null;
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/StringBlock.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2011 Ryszard Wiśniewski
3 | * Modified Copyright 2015 ZhaoHai <2801045898@qq.com>
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package zhao.arsceditor.ResDecoder;
19 |
20 | import java.io.ByteArrayOutputStream;
21 | import java.io.IOException;
22 | import java.nio.ByteBuffer;
23 | import java.nio.charset.CharacterCodingException;
24 | import java.nio.charset.Charset;
25 | import java.nio.charset.CharsetDecoder;
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import zhao.arsceditor.ResDecoder.IO.LEDataInputStream;
30 | import zhao.arsceditor.ResDecoder.IO.LEDataOutputStream;
31 |
32 |
33 | /**
34 | * @author Ryszard Wiśniewski
35 | * @author Dmitry Skiba
36 | * @author ZhaoHai
37 | *
38 | * Block of strings, used in binary xml and arsc.
39 | *
40 | * TODO: - implement get()
41 | *
42 | */
43 | public class StringBlock {
44 |
45 | private static final int CHUNK_NULL_TYPE = 0x00000000;
46 |
47 | private static final int CHUNK_STRINGPOOL_TYPE = 0x001C0001;
48 |
49 | // UTF-16解码器
50 | private static final CharsetDecoder UTF16LE_DECODER = Charset.forName("UTF-16LE").newDecoder();
51 |
52 | // UTF-8解码器
53 | private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8").newDecoder();
54 |
55 | private static final int UTF8_FLAG = 0x00000100;
56 |
57 | private static final int getShort(byte[] array, int offset) {
58 | return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
59 | }
60 |
61 | private static final int[] getVarint(byte[] array, int offset) {
62 | int val = array[offset];
63 | boolean more = (val & 0x80) != 0;
64 | val &= 0x7f;
65 |
66 | if (!more) {
67 | return new int[] { val, 1 };
68 | } else {
69 | return new int[] { val << 8 | array[offset + 1] & 0xff, 2 };
70 | }
71 | }
72 |
73 | /**
74 | * Reads whole (including chunk type) string block from stream. Stream must
75 | * be at the chunk type.
76 | */
77 | public static StringBlock read(LEDataInputStream reader) throws IOException {
78 | StringBlock block = new StringBlock();
79 |
80 | block.ChunkTypeInt = reader.skipCheckChunkTypeInt(CHUNK_STRINGPOOL_TYPE, CHUNK_NULL_TYPE);
81 | // 获取块大小
82 | block.chunkSize = reader.readInt();
83 | // System.out.println("chunkSize=" + block.chunkSize);
84 | // 获取字符串数量
85 | block.stringCount = reader.readInt();
86 | // System.out.println("stringCount=" + block.stringCount);
87 | // 获取字符串样式偏移地址数量
88 | block.styleOffsetCount = reader.readInt();
89 | // System.out.println("styleOffsetCount=" + block.styleOffsetCount);
90 | //// 用来描述字符串资源串的属性
91 | block.flags = reader.readInt();
92 | // System.out.println("flag=" + block.flags);
93 | // 获取字符串偏移的地址
94 | block.stringsOffset = reader.readInt();
95 | // System.out.println("stringsOffset=" + block.stringsOffset);
96 | // 获取字符串样式偏移的地址
97 | block.stylesOffset = reader.readInt();
98 | // System.out.println("stylesOffset=" + block.stylesOffset);
99 | // 根据flags判断是否是UTF-8编码
100 | block.m_isUTF8 = (block.flags & UTF8_FLAG) != 0;
101 | // 获取所有的字符串偏移的地址,储存到一个数组中
102 | block.m_stringOffsets = reader.readIntArray(block.stringCount);
103 |
104 | // 如果字符串样式偏移的数量不为0
105 | if (block.styleOffsetCount != 0) {
106 | // 获取所有字符串样式的偏移
107 | block.m_styleOffsets = reader.readIntArray(block.styleOffsetCount);
108 | // System.out.println("m_styleOffsets size=" +
109 | // block.m_styleOffsets.length);
110 | }
111 | // 使用局部变量,需要加个{},以免和全局变量混到一起
112 | {
113 | int size = ((block.stylesOffset == 0) ? block.chunkSize : block.stylesOffset) - block.stringsOffset;
114 | if ((size % 4) != 0) {
115 | throw new IOException("String data size is not multiple of 4 (" + size + ").");
116 | }
117 | block.m_strings = new byte[size];
118 | reader.readFully(block.m_strings);
119 | // System.out.println("m_strings size=" + size);
120 |
121 | block.strings = new ArrayList();
122 | for (int i = 0; i < block.stringCount; i++) {
123 | block.strings.add(block.getString(i));
124 | }
125 | }
126 | // 如果字符串样式偏移不为0
127 | if (block.stylesOffset != 0) {
128 | int size = (block.chunkSize - block.stylesOffset);
129 | if ((size % 4) != 0) {
130 | throw new IOException("Style data size is not multiple of 4 (" + size + ").");
131 | }
132 | block.m_styles = reader.readIntArray(size / 4);
133 | // System.out.println("m_styles size=" + block.m_styles.length);
134 | // read remaining bytes
135 | int remaining = size % 4;
136 | if (remaining >= 1) {
137 | while (remaining-- > 0) {
138 | reader.skipByte();
139 | }
140 | }
141 | }
142 |
143 | return block;
144 | }
145 |
146 | // chunkSize
147 | private int chunkSize;
148 |
149 | //
150 | private int ChunkTypeInt;
151 |
152 | // 用来描述字符串资源串的属性
153 | private int flags;
154 |
155 | // 是否是UTF-8编码
156 | private boolean m_isUTF8;
157 |
158 | // 所有字符串的偏移
159 | private int[] m_stringOffsets;
160 | // 所有字符串的字节数组
161 | public byte[] m_strings;
162 |
163 | // 所有字符串样式的偏移
164 | private int[] m_styleOffsets;
165 | // 所有字符串样式的整型数组
166 | private int[] m_styles;
167 | // 字符串数量
168 | private int stringCount;
169 | // 所有新的字符串的偏移
170 | private int[] stringOffsets;
171 | // 解析出的字符串
172 | private List strings;
173 | // 字符串开始偏移的位置
174 | private int stringsOffset;
175 | // 字符串样式偏移的数量
176 | private int styleOffsetCount;
177 | // 字符串样式开始偏移的位置
178 | private int stylesOffset;
179 |
180 | // 解析字符串
181 | private String decodeString(int offset, int length) throws CharacterCodingException {
182 | return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(ByteBuffer.wrap(m_strings, offset, length))
183 | .toString();
184 |
185 | }
186 |
187 | /**
188 | * Finds index of the string. Returns -1 if the string was not found.
189 | */
190 | public int find(String string) {
191 | if (string == null) {
192 | return -1;
193 | }
194 | for (int i = 0; i != m_stringOffsets.length; ++i) {
195 | int offset = m_stringOffsets[i];
196 | int length = getShort(m_strings, offset);
197 | if (length != string.length()) {
198 | continue;
199 | }
200 | int j = 0;
201 | for (; j != length; ++j) {
202 | offset += 2;
203 | if (string.charAt(j) != getShort(m_strings, offset)) {
204 | break;
205 | }
206 | }
207 | if (j == length) {
208 | return i;
209 | }
210 | }
211 | return -1;
212 | }
213 |
214 | /**
215 | * 获取字符串数量
216 | */
217 | public int getCount() {
218 | return m_stringOffsets != null ? m_stringOffsets.length : 0;
219 | }
220 |
221 | // 获取排序完成后的列表数组,其中包含了修改后的内容,写入文件时,我们只需将里面的字符串按顺序写入即可
222 | public List getList() {
223 | return strings;
224 | }
225 |
226 | /**
227 | * 获取常量池中第index个字符串的方法
228 | *
229 | * @throws CharacterCodingException
230 | */
231 | public String getString(int index) throws CharacterCodingException {
232 | // 如果传入的数组下标小于0,或者是字符串地址数组为空或者传入的数组下标大于地址数组的成员个数
233 | if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) {
234 | // 返回空值
235 | return null;
236 | }
237 | // 获取第index个字符串的起始地址
238 | int offset = m_stringOffsets[index];
239 | // 第index个字符串的长度
240 | int length;
241 | // 如果第index个字符串不是UTF-8型,是UTF-16型
242 | if (!m_isUTF8) {
243 | // 获取该字符串的长度
244 | length = getShort(m_strings, offset) * 2;
245 | // 地址偏移2
246 | offset += 2;
247 | } else { // 如果是UTF-8型
248 | // 地址偏移getVarint(this.m_strings, offset)[1]
249 | offset += getVarint(m_strings, offset)[1];
250 | int[] varint = getVarint(m_strings, offset);
251 | // 地址再次偏移varint[1]
252 | offset += varint[1];
253 | // 获取字符串长度
254 | length = varint[0];
255 | }
256 | // 从offest地址处解析length长度个数据,转化为String类型返回
257 | return decodeString(offset, length);
258 | }
259 |
260 | // 排序字符串,由于字符串在arsc中是一一对应的,所以不能改变原来的一一对应,需要将列表进行排序
261 | public void sortStringBlock(String src, String tar) {
262 | // 查找原字符串在strings这个有序的数组中的位置,以此确定修改后的字符串对应的位置
263 | int position = strings.indexOf(src);
264 | // 如果在strings中查找到这个字符串,并且修改后的字符串不为空
265 | if (position >= 0 && !tar.equals("")) {
266 | // 从strings数组中移除原来的内容
267 | // strings.remove(position);
268 | // 将新字符串添加到相应的位置
269 | strings.set(position, tar);
270 | }
271 | }
272 |
273 | public void writeFully(LEDataOutputStream lmOut, ByteArrayOutputStream bOut) throws IOException {
274 | // 新字符串样式的偏移
275 | int newStylesOffset = 0;
276 | // 新的chunkSize
277 | int newChunkSize = chunkSize;
278 | // System.out.println("src chunksize=" + chunkSize);
279 | // 如果字符串样式偏移为0
280 | if (stylesOffset == 0)
281 | // 获取新的chunkSize,经过试验发现,chunkSize=原先的chunkSize +
282 | // 修改后的字符串占用的字节-原来字符串占用的字节
283 | newChunkSize = newChunkSize + (bOut.size() - m_strings.length);
284 | else { // 如果字符串样式偏移不为0
285 | // 获取新的StylesOffset,经过试验发现,StylesOffset=原先的chunkSize +
286 | // 修改后的字符串占用的字节-原来字符串占用的字节
287 | newChunkSize = newChunkSize + (bOut.size() - m_strings.length);
288 | newStylesOffset = stylesOffset + (bOut.size() - m_strings.length);
289 | }
290 | // System.out.println("bOutSize=" + bOut.size());
291 | lmOut.writeInt(ChunkTypeInt);
292 | // chunkSize
293 | lmOut.writeInt(newChunkSize);
294 | // System.out.println("new chunk size==" + newChunkSize);
295 | // 写入字符串数量
296 | lmOut.writeInt(stringCount);
297 | // System.out.println("stringCount==" + stringCount);
298 | // 写入字符串样式数量
299 | lmOut.writeInt(styleOffsetCount);
300 | // System.out.println("styleOffsetCount==" + styleOffsetCount);
301 | // 写入资源属性
302 | lmOut.writeInt(flags);
303 | // System.out.println("flags==" + flags);
304 | // 写入字符串偏移的地址
305 | lmOut.writeInt(stringsOffset);
306 | // System.out.println("stringOffset==" + stringOffsets);
307 | // 写入字符串样式偏移的地址
308 | lmOut.writeInt(newStylesOffset);
309 | // System.out.println("newstyleOffset==" + newStylesOffset);
310 | // 写入所有字符串一一对应的地址
311 | lmOut.writeIntArray(stringOffsets);
312 | // System.out.println("styleOffsetCount==" + styleOffsetCount);
313 | // 如果字符串样式偏移的数量不为0
314 | if (styleOffsetCount != 0) {
315 | // 写入所有字符串样式一一对应的地址偏移
316 | lmOut.writeIntArray(m_styleOffsets);
317 | }
318 | // 写入字符串
319 | lmOut.writeFully(bOut.toByteArray());
320 |
321 | if (stylesOffset != 0) {
322 | // 写入字符串样式
323 | lmOut.writeIntArray(m_styles);
324 | int size = (chunkSize - stylesOffset);
325 | // read remaining bytes
326 | int remaining = size % 4;
327 | if (remaining >= 1) {
328 | while (remaining-- > 0) {
329 | // 写入剩余内容
330 | lmOut.writeByte((byte) 0);
331 | }
332 | }
333 | }
334 | }
335 |
336 | public ByteArrayOutputStream writeString(List stringlist) throws IOException {
337 | ByteArrayOutputStream bOut = new ByteArrayOutputStream();
338 | LEDataOutputStream mStrings = new LEDataOutputStream(bOut);
339 |
340 | // 字符串数量
341 | int size = stringlist.size();
342 |
343 | // 每串字符串在文件中的偏移位置
344 | stringOffsets = new int[size];
345 | // 字符串的长度
346 | int len = 0;
347 |
348 | for (int i = 0; i < size; i++) {
349 | // 获取列表中的字符串
350 | String var = stringlist.get(i);
351 | // 获取字符串的长度
352 | int length = var.length();
353 | // 获取字符串的偏移
354 | stringOffsets[i] = len;
355 | // 将字符串写入流中
356 | writeString(var, mStrings);
357 | // 如果第index个字符串是UTF-16型
358 | if ((flags & UTF8_FLAG) == 0) {
359 | // 为什么要大于这个数,这是我分析出来的
360 | if (length > 0x00007fff) {
361 | // 字符串在文件中储存的长度偏移2
362 | len += 2;
363 | }
364 | // 字符串在文件中储存的长度偏移2
365 | len += 2;
366 | // 字符串在文件中储存的长度偏移该字符串占用的字节的长度
367 | len += var.getBytes("UTF-16LE").length;
368 | // 字符串在文件中储存的长度偏移2
369 | len += 2;
370 | } else { // 如果是UTF-8型
371 | if (length > 0x0000007f) {
372 | // 字符串在文件中储存的长度偏移1
373 | len += 1;
374 | }
375 | // 字符串在文件中储存的长度偏移1
376 | len += 1;
377 | // 获取字符串在内存中的字节
378 | byte[] bytes = var.getBytes("UTF8");
379 | // 获取该字符串占用字节的大小
380 | length = bytes.length;
381 | if (length > 0x0000007f) {
382 | // 字符串在文件中储存的长度偏移1
383 | len += 1;
384 | }
385 | // 字符串在文件中储存的长度偏移1
386 | len += 1;
387 | // 字符串在文件中储存的长度偏移该字符串占用的字节的长度
388 | len += length;
389 | // 字符串在文件中储存的长度偏移1
390 | len += 1;
391 | }
392 | }
393 |
394 | // 偏移 0
395 | /*
396 | * while (mStrings.size()%4!=0) { mStrings.writeByte((byte)0); }
397 | */
398 | int size_mod = mStrings.size() % 4;// m_strings_size%4
399 | // 写入一些0字节用来填充,以确保arsc文件的正确性
400 | for (int i = 0; i < 4 - size_mod; i++) {
401 | mStrings.writeByte((byte) 0);
402 | }
403 | bOut.close();
404 | return bOut;
405 | }
406 |
407 | /**
408 | * 写入第index个字符串的方法
409 | *
410 | * @throws IOException
411 | */
412 | private void writeString(String str, LEDataOutputStream lmString) throws IOException {
413 | // 获取字符串长度
414 | int length = str.length();
415 | // 如果第index个字符串是UTF-16型
416 | if ((flags & UTF8_FLAG) == 0) {
417 | // 该算法是我从汇编中获取的
418 | if (length > 0x00007fff) {
419 | int i5 = 0x00008000 | (length >> 16);
420 | lmString.writeByte((byte) i5);
421 | lmString.writeByte((byte) (i5 >> 8));
422 | }
423 | lmString.writeByte((byte) length);
424 | lmString.writeByte((byte) (length >> 8));
425 | lmString.writeFully(str.getBytes("UTF-16LE"));
426 | lmString.writeByte((byte) 0);
427 | lmString.writeByte((byte) 0);
428 | } else { // 如果是UTF-8型
429 | if (length > 0x0000007f) {
430 | lmString.writeByte((byte) ((length >> 8) | 0x00000080));
431 | }
432 | lmString.writeByte((byte) length);
433 | byte[] bytes = str.getBytes("UTF8");
434 | length = bytes.length;
435 | if (length > 0x0000007f) {
436 | lmString.writeByte((byte) ((length >> 8) | 0x00000080));
437 | }
438 | lmString.writeByte((byte) length);
439 |
440 | lmString.writeFully(bytes);
441 | lmString.writeByte((byte) 0);
442 | }
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/ResDecoder/data/ResConfigFlags.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2014 Ryszard Wiśniewski
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package zhao.arsceditor.ResDecoder.data;
18 |
19 | import java.util.logging.Logger;
20 |
21 | /**
22 | * @author Ryszard Wiśniewski
23 | */
24 | public class ResConfigFlags {
25 | public final static int DENSITY_400 = 190;
26 | public final static int DENSITY_ANY = 0xFFFE;
27 |
28 | public final static int DENSITY_DEFAULT = 0;
29 | public final static int DENSITY_HIGH = 240;
30 |
31 | public final static int DENSITY_LOW = 120;
32 | public final static int DENSITY_MEDIUM = 160;
33 | public final static int DENSITY_NONE = 0xFFFF;
34 |
35 | public final static int DENSITY_TV = 213;
36 | public final static int DENSITY_XHIGH = 320;
37 | public final static int DENSITY_XXHIGH = 480;
38 |
39 | public final static int DENSITY_XXXHIGH = 640;
40 | public final static byte KEYBOARD_12KEY = 3;
41 |
42 | public final static byte KEYBOARD_ANY = 0;
43 |
44 | public final static byte KEYBOARD_NOKEYS = 1;
45 | public final static byte KEYBOARD_QWERTY = 2;
46 | public final static byte KEYSHIDDEN_ANY = 0x0;
47 |
48 | public final static byte KEYSHIDDEN_NO = 0x1;
49 | public final static byte KEYSHIDDEN_SOFT = 0x3;
50 |
51 | public final static byte KEYSHIDDEN_YES = 0x2;
52 | private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class.getName());
53 |
54 | public final static byte MASK_KEYSHIDDEN = 0x3;
55 |
56 | public final static short MASK_LAYOUTDIR = 0xc0;
57 |
58 | public final static byte MASK_NAVHIDDEN = 0xc;
59 |
60 | public final static byte MASK_SCREENLONG = 0x30;
61 |
62 | public final static short MASK_SCREENROUND = 0x03;
63 |
64 | public final static byte MASK_SCREENSIZE = 0x0f;
65 |
66 | public final static byte MASK_UI_MODE_NIGHT = 0x30;
67 |
68 | public final static byte MASK_UI_MODE_TYPE = 0x0f;
69 |
70 | public final static int MNC_ZERO = -1;
71 |
72 | public final static byte NAVHIDDEN_ANY = 0x0;
73 |
74 | public final static byte NAVHIDDEN_NO = 0x4;
75 |
76 | public final static byte NAVHIDDEN_YES = 0x8;
77 |
78 | public final static byte NAVIGATION_ANY = 0;
79 |
80 | public final static byte NAVIGATION_DPAD = 2;
81 |
82 | public final static byte NAVIGATION_NONAV = 1;
83 |
84 | public final static byte NAVIGATION_TRACKBALL = 3;
85 | public final static byte NAVIGATION_WHEEL = 4;
86 | public final static byte ORIENTATION_ANY = 0;
87 | public final static byte ORIENTATION_LAND = 2;
88 | public final static byte ORIENTATION_PORT = 1;
89 | public final static byte ORIENTATION_SQUARE = 3;
90 | public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
91 | public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
92 | public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
93 | public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
94 | public final static short SCREENLAYOUT_ROUND_ANY = 0;
95 | public final static short SCREENLAYOUT_ROUND_NO = 0x1;
96 | public final static short SCREENLAYOUT_ROUND_YES = 0x2;
97 | public final static byte SCREENLONG_ANY = 0x00;
98 | public final static byte SCREENLONG_NO = 0x10;
99 | public final static byte SCREENLONG_YES = 0x20;
100 | public final static byte SCREENSIZE_ANY = 0x00;
101 | public final static byte SCREENSIZE_LARGE = 0x03;
102 | public final static byte SCREENSIZE_NORMAL = 0x02;
103 | public final static byte SCREENSIZE_SMALL = 0x01;
104 | public final static byte SCREENSIZE_XLARGE = 0x04;
105 | public final static byte SDK_BASE = 1;
106 |
107 | public final static byte SDK_BASE_1_1 = 2;
108 | public final static byte SDK_CUPCAKE = 3;
109 | public final static byte SDK_DONUT = 4;
110 | public final static byte SDK_ECLAIR = 5;
111 |
112 | public final static byte SDK_ECLAIR_0_1 = 6;
113 | public final static byte SDK_ECLAIR_MR1 = 7;
114 | public final static byte SDK_FROYO = 8;
115 | public final static byte SDK_GINGERBREAD = 9;
116 |
117 | public final static byte SDK_GINGERBREAD_MR1 = 10;
118 | public final static byte SDK_HONEYCOMB = 11;
119 | public final static byte SDK_HONEYCOMB_MR1 = 12;
120 | public final static byte SDK_HONEYCOMB_MR2 = 13;
121 | public final static byte SDK_ICE_CREAM_SANDWICH = 14;
122 | public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
123 | public final static byte SDK_JELLY_BEAN = 16;
124 | public final static byte SDK_JELLY_BEAN_MR1 = 17;
125 | public final static byte SDK_JELLY_BEAN_MR2 = 18;
126 | public final static byte SDK_KITKAT = 19;
127 | public final static byte SDK_LOLLIPOP = 21;
128 |
129 | public final static byte SDK_LOLLIPOP_MR1 = 22;
130 |
131 | public final static byte SDK_MNC = 23;
132 | // TODO: Dirty static hack. This counter should be a part of ResPackage,
133 | // but it would be hard right now and this feature is very rarely used.
134 | private static int sErrCounter = 0;
135 | public final static byte TOUCHSCREEN_ANY = 0;
136 | public final static byte TOUCHSCREEN_FINGER = 3;
137 | public final static byte TOUCHSCREEN_NOTOUCH = 1;
138 |
139 | public final static byte TOUCHSCREEN_STYLUS = 2;
140 | public final static byte UI_MODE_NIGHT_ANY = 0x00;
141 | public final static byte UI_MODE_NIGHT_NO = 0x10;
142 | public final static byte UI_MODE_NIGHT_YES = 0x20;
143 |
144 | public final static byte UI_MODE_TYPE_ANY = 0x00;
145 | public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
146 | public final static byte UI_MODE_TYPE_CAR = 0x03;
147 | public final static byte UI_MODE_TYPE_DESK = 0x02;
148 |
149 | // start - miui
150 | public final static byte UI_MODE_TYPE_GODZILLAUI = 0x0b;
151 | public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
152 | // end - miui
153 | public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
154 | public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
155 | public final static byte UI_MODE_TYPE_NORMAL = 0x01;
156 |
157 | public final static byte UI_MODE_TYPE_SMALLUI = 0x0c;
158 | public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
159 | public final static byte UI_MODE_TYPE_WATCH = 0x06;
160 | public final int density;
161 | public final byte inputFlags;
162 |
163 | public final boolean isInvalid;
164 | public final byte keyboard;
165 | public final char[] language;
166 | private final char[] localeScript;
167 |
168 | private final char[] localeVariant;
169 | public final short mcc;
170 | public final short mnc;
171 | private final String mQualifiers;
172 | public final byte navigation;
173 | public final byte orientation;
174 |
175 | public final char[] region;
176 | public final short screenHeight;
177 | public final short screenHeightDp;
178 | public final byte screenLayout;
179 |
180 | private final byte screenLayout2;
181 | public final short screenWidth;
182 | public final short screenWidthDp;
183 | public final short sdkVersion;
184 | private final int size;
185 | public final short smallestScreenWidthDp;
186 | public final byte touchscreen;
187 | public final byte uiMode;
188 |
189 | public ResConfigFlags() {
190 | mcc = 0;
191 | mnc = 0;
192 | language = new char[] { '\00', '\00' };
193 | region = new char[] { '\00', '\00' };
194 | orientation = ORIENTATION_ANY;
195 | touchscreen = TOUCHSCREEN_ANY;
196 | density = DENSITY_DEFAULT;
197 | keyboard = KEYBOARD_ANY;
198 | navigation = NAVIGATION_ANY;
199 | inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
200 | screenWidth = 0;
201 | screenHeight = 0;
202 | sdkVersion = 0;
203 | screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
204 | uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
205 | smallestScreenWidthDp = 0;
206 | screenWidthDp = 0;
207 | screenHeightDp = 0;
208 | localeScript = null;
209 | localeVariant = null;
210 | screenLayout2 = 0;
211 | isInvalid = false;
212 | mQualifiers = "";
213 | size = 0;
214 | }
215 |
216 | public ResConfigFlags(short mcc, short mnc, char[] language, char[] region, byte orientation, byte touchscreen,
217 | int density, byte keyboard, byte navigation, byte inputFlags, short screenWidth, short screenHeight,
218 | short sdkVersion, byte screenLayout, byte uiMode, short smallestScreenWidthDp, short screenWidthDp,
219 | short screenHeightDp, char[] localeScript, char[] localeVariant, byte screenLayout2, boolean isInvalid,
220 | int size) {
221 | if (orientation < 0 || orientation > 3) {
222 | LOGGER.warning("Invalid orientation value: " + orientation);
223 | orientation = 0;
224 | isInvalid = true;
225 | }
226 | if (touchscreen < 0 || touchscreen > 3) {
227 | LOGGER.warning("Invalid touchscreen value: " + touchscreen);
228 | touchscreen = 0;
229 | isInvalid = true;
230 | }
231 | if (density < -1) {
232 | LOGGER.warning("Invalid density value: " + density);
233 | density = 0;
234 | isInvalid = true;
235 | }
236 | if (keyboard < 0 || keyboard > 3) {
237 | LOGGER.warning("Invalid keyboard value: " + keyboard);
238 | keyboard = 0;
239 | isInvalid = true;
240 | }
241 | if (navigation < 0 || navigation > 4) {
242 | LOGGER.warning("Invalid navigation value: " + navigation);
243 | navigation = 0;
244 | isInvalid = true;
245 | }
246 |
247 | if (localeScript != null && localeScript.length != 0) {
248 | if (localeScript[0] == '\00') {
249 | localeScript = null;
250 | }
251 | } else {
252 | localeScript = null;
253 | }
254 |
255 | if (localeVariant != null && localeVariant.length != 0) {
256 | if (localeVariant[0] == '\00') {
257 | localeVariant = null;
258 | }
259 | } else {
260 | localeVariant = null;
261 | }
262 |
263 | this.mcc = mcc;
264 | this.mnc = mnc;
265 | this.language = language;
266 | this.region = region;
267 | this.orientation = orientation;
268 | this.touchscreen = touchscreen;
269 | this.density = density;
270 | this.keyboard = keyboard;
271 | this.navigation = navigation;
272 | this.inputFlags = inputFlags;
273 | this.screenWidth = screenWidth;
274 | this.screenHeight = screenHeight;
275 | this.sdkVersion = sdkVersion;
276 | this.screenLayout = screenLayout;
277 | this.uiMode = uiMode;
278 | this.smallestScreenWidthDp = smallestScreenWidthDp;
279 | this.screenWidthDp = screenWidthDp;
280 | this.screenHeightDp = screenHeightDp;
281 | this.localeScript = localeScript;
282 | this.localeVariant = localeVariant;
283 | this.screenLayout2 = screenLayout2;
284 | this.isInvalid = isInvalid;
285 | this.size = size;
286 | mQualifiers = generateQualifiers();
287 | }
288 |
289 | @Override
290 | public boolean equals(Object obj) {
291 | if (obj == null) {
292 | return false;
293 | }
294 | if (getClass() != obj.getClass()) {
295 | return false;
296 | }
297 | final ResConfigFlags other = (ResConfigFlags) obj;
298 | return this.mQualifiers.equals(other.mQualifiers);
299 | }
300 |
301 | private String generateQualifiers() {
302 | StringBuilder ret = new StringBuilder();
303 | if (mcc != 0) {
304 | ret.append("-mcc").append(String.format("%03d", mcc));
305 | if (mnc != MNC_ZERO) {
306 | if (mnc != 0) {
307 | ret.append("-mnc");
308 | if (size <= 32) {
309 | if (mnc > 0 && mnc < 10) {
310 | ret.append(String.format("%02d", mnc));
311 | } else {
312 | ret.append(String.format("%03d", mnc));
313 | }
314 | } else {
315 | ret.append(mnc);
316 | }
317 | }
318 | } else {
319 | ret.append("-mnc00");
320 | }
321 | } else {
322 | if (mnc != 0) {
323 | ret.append("-mnc").append(mnc);
324 | }
325 | }
326 | ret.append(getLocaleString());
327 |
328 | switch (screenLayout & MASK_LAYOUTDIR) {
329 | case SCREENLAYOUT_LAYOUTDIR_RTL:
330 | ret.append("-ldrtl");
331 | break;
332 | case SCREENLAYOUT_LAYOUTDIR_LTR:
333 | ret.append("-ldltr");
334 | break;
335 | }
336 | if (smallestScreenWidthDp != 0) {
337 | ret.append("-sw").append(smallestScreenWidthDp).append("dp");
338 | }
339 | if (screenWidthDp != 0) {
340 | ret.append("-w").append(screenWidthDp).append("dp");
341 | }
342 | if (screenHeightDp != 0) {
343 | ret.append("-h").append(screenHeightDp).append("dp");
344 | }
345 | switch (screenLayout & MASK_SCREENSIZE) {
346 | case SCREENSIZE_SMALL:
347 | ret.append("-small");
348 | break;
349 | case SCREENSIZE_NORMAL:
350 | ret.append("-normal");
351 | break;
352 | case SCREENSIZE_LARGE:
353 | ret.append("-large");
354 | break;
355 | case SCREENSIZE_XLARGE:
356 | ret.append("-xlarge");
357 | break;
358 | }
359 | switch (screenLayout & MASK_SCREENLONG) {
360 | case SCREENLONG_YES:
361 | ret.append("-long");
362 | break;
363 | case SCREENLONG_NO:
364 | ret.append("-notlong");
365 | break;
366 | }
367 | switch (screenLayout2 & MASK_SCREENROUND) {
368 | case SCREENLAYOUT_ROUND_NO:
369 | ret.append("-notround");
370 | break;
371 | case SCREENLAYOUT_ROUND_YES:
372 | ret.append("-round");
373 | break;
374 | }
375 | switch (orientation) {
376 | case ORIENTATION_PORT:
377 | ret.append("-port");
378 | break;
379 | case ORIENTATION_LAND:
380 | ret.append("-land");
381 | break;
382 | case ORIENTATION_SQUARE:
383 | ret.append("-square");
384 | break;
385 | }
386 | switch (uiMode & MASK_UI_MODE_TYPE) {
387 | case UI_MODE_TYPE_CAR:
388 | ret.append("-car");
389 | break;
390 | case UI_MODE_TYPE_DESK:
391 | ret.append("-desk");
392 | break;
393 | case UI_MODE_TYPE_TELEVISION:
394 | ret.append("-television");
395 | break;
396 | case UI_MODE_TYPE_SMALLUI:
397 | ret.append("-smallui");
398 | break;
399 | case UI_MODE_TYPE_MEDIUMUI:
400 | ret.append("-mediumui");
401 | break;
402 | case UI_MODE_TYPE_LARGEUI:
403 | ret.append("-largeui");
404 | break;
405 | case UI_MODE_TYPE_GODZILLAUI:
406 | ret.append("-godzillaui");
407 | break;
408 | case UI_MODE_TYPE_HUGEUI:
409 | ret.append("-hugeui");
410 | break;
411 | case UI_MODE_TYPE_APPLIANCE:
412 | ret.append("-appliance");
413 | break;
414 | case UI_MODE_TYPE_WATCH:
415 | ret.append("-watch");
416 | break;
417 | }
418 | switch (uiMode & MASK_UI_MODE_NIGHT) {
419 | case UI_MODE_NIGHT_YES:
420 | ret.append("-night");
421 | break;
422 | case UI_MODE_NIGHT_NO:
423 | ret.append("-notnight");
424 | break;
425 | }
426 | switch (density) {
427 | case DENSITY_DEFAULT:
428 | break;
429 | case DENSITY_LOW:
430 | ret.append("-ldpi");
431 | break;
432 | case DENSITY_MEDIUM:
433 | ret.append("-mdpi");
434 | break;
435 | case DENSITY_HIGH:
436 | ret.append("-hdpi");
437 | break;
438 | case DENSITY_TV:
439 | ret.append("-tvdpi");
440 | break;
441 | case DENSITY_XHIGH:
442 | ret.append("-xhdpi");
443 | break;
444 | case DENSITY_XXHIGH:
445 | ret.append("-xxhdpi");
446 | break;
447 | case DENSITY_XXXHIGH:
448 | ret.append("-xxxhdpi");
449 | break;
450 | case DENSITY_ANY:
451 | ret.append("-anydpi");
452 | break;
453 | case DENSITY_NONE:
454 | ret.append("-nodpi");
455 | break;
456 | default:
457 | ret.append('-').append(density).append("dpi");
458 | }
459 | switch (touchscreen) {
460 | case TOUCHSCREEN_NOTOUCH:
461 | ret.append("-notouch");
462 | break;
463 | case TOUCHSCREEN_STYLUS:
464 | ret.append("-stylus");
465 | break;
466 | case TOUCHSCREEN_FINGER:
467 | ret.append("-finger");
468 | break;
469 | }
470 | switch (inputFlags & MASK_KEYSHIDDEN) {
471 | case KEYSHIDDEN_NO:
472 | ret.append("-keysexposed");
473 | break;
474 | case KEYSHIDDEN_YES:
475 | ret.append("-keyshidden");
476 | break;
477 | case KEYSHIDDEN_SOFT:
478 | ret.append("-keyssoft");
479 | break;
480 | }
481 | switch (keyboard) {
482 | case KEYBOARD_NOKEYS:
483 | ret.append("-nokeys");
484 | break;
485 | case KEYBOARD_QWERTY:
486 | ret.append("-qwerty");
487 | break;
488 | case KEYBOARD_12KEY:
489 | ret.append("-12key");
490 | break;
491 | }
492 | switch (inputFlags & MASK_NAVHIDDEN) {
493 | case NAVHIDDEN_NO:
494 | ret.append("-navexposed");
495 | break;
496 | case NAVHIDDEN_YES:
497 | ret.append("-navhidden");
498 | break;
499 | }
500 | switch (navigation) {
501 | case NAVIGATION_NONAV:
502 | ret.append("-nonav");
503 | break;
504 | case NAVIGATION_DPAD:
505 | ret.append("-dpad");
506 | break;
507 | case NAVIGATION_TRACKBALL:
508 | ret.append("-trackball");
509 | break;
510 | case NAVIGATION_WHEEL:
511 | ret.append("-wheel");
512 | break;
513 | }
514 | if (screenWidth != 0 && screenHeight != 0) {
515 | if (screenWidth > screenHeight) {
516 | ret.append(String.format("-%dx%d", screenWidth, screenHeight));
517 | } else {
518 | ret.append(String.format("-%dx%d", screenHeight, screenWidth));
519 | }
520 | }
521 | if (sdkVersion > 0 && sdkVersion >= getNaturalSdkVersionRequirement()) {
522 | ret.append("-v").append(sdkVersion);
523 | }
524 | if (isInvalid) {
525 | ret.append("-ERR").append(sErrCounter++);
526 | }
527 |
528 | return ret.toString();
529 | }
530 |
531 | private String getLocaleString() {
532 | StringBuilder sb = new StringBuilder();
533 |
534 | // check for old style non BCP47 tags
535 | // allows values-xx-rXX, values-xx, values-xxx-rXX
536 | // denies values-xxx, anything else
537 | if (localeVariant == null && localeScript == null && (region[0] != '\00' || language[0] != '\00')
538 | && region.length != 3) {
539 | sb.append("-").append(language);
540 | if (region[0] != '\00') {
541 | sb.append("-r").append(region);
542 | }
543 | } else { // BCP47
544 | if (language[0] == '\00' && region[0] == '\00') {
545 | return sb.toString(); // early return, no language or region
546 | }
547 | sb.append("-b+");
548 | if (language[0] != '\00') {
549 | sb.append(language);
550 | }
551 | if (localeScript != null && localeScript.length == 4) {
552 | sb.append("+").append(localeScript);
553 | }
554 | if ((region.length == 2 || region.length == 3) && region[0] != '\00') {
555 | sb.append("+").append(region);
556 | }
557 | if (localeVariant != null && localeVariant.length >= 5) {
558 | sb.append("+").append(toUpper(localeVariant));
559 | }
560 | }
561 | return sb.toString();
562 | }
563 |
564 | private short getNaturalSdkVersionRequirement() {
565 | if ((screenLayout2 & MASK_SCREENROUND) != 0) {
566 | return SDK_MNC;
567 | }
568 | if (density == DENSITY_ANY) {
569 | return SDK_LOLLIPOP;
570 | }
571 | if (smallestScreenWidthDp != 0 || screenWidthDp != 0 || screenHeightDp != 0) {
572 | return SDK_HONEYCOMB_MR2;
573 | }
574 | if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != UI_MODE_NIGHT_ANY) {
575 | return SDK_FROYO;
576 | }
577 | if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != SCREENSIZE_ANY || density != DENSITY_DEFAULT) {
578 | return SDK_DONUT;
579 | }
580 | return 0;
581 | }
582 |
583 | public String getQualifiers() {
584 | return mQualifiers;
585 | }
586 |
587 | @Override
588 | public int hashCode() {
589 | int hash = 17;
590 | hash = 31 * hash + this.mQualifiers.hashCode();
591 | return hash;
592 | }
593 |
594 | @Override
595 | public String toString() {
596 | return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
597 | }
598 |
599 | private String toUpper(char[] character) {
600 | StringBuilder sb = new StringBuilder();
601 | for (char ch : character) {
602 | sb.append(Character.toUpperCase(ch));
603 | }
604 | return sb.toString();
605 | }
606 | }
607 |
--------------------------------------------------------------------------------
/src/zhao/arsceditor/MainActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 ZhaoHai <2801045898@qq.com>
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * @author zhaohai
19 | * @time 2015.10
20 | * ARSC编辑器主界面
21 | * */
22 | package zhao.arsceditor;
23 |
24 | import java.io.FileInputStream;
25 | import java.io.FileOutputStream;
26 | import java.io.IOException;
27 | import java.io.InputStream;
28 | import java.nio.charset.CharacterCodingException;
29 | import java.util.ArrayList;
30 | import java.util.Collections;
31 | import java.util.List;
32 |
33 | import android.annotation.SuppressLint;
34 | import android.app.Activity;
35 | import android.app.AlertDialog;
36 | import android.app.ProgressDialog;
37 | import android.content.ContentValues;
38 | import android.content.Context;
39 | import android.content.DialogInterface;
40 | import android.os.AsyncTask;
41 | import android.os.Bundle;
42 | import android.text.Editable;
43 | import android.text.TextWatcher;
44 | import android.util.Log;
45 | import android.view.LayoutInflater;
46 | import android.view.MotionEvent;
47 | import android.view.View;
48 | import android.view.View.OnClickListener;
49 | import android.view.View.OnTouchListener;
50 | import android.view.ViewGroup;
51 | import android.widget.AdapterView;
52 | import android.widget.AdapterView.OnItemLongClickListener;
53 | import android.widget.BaseAdapter;
54 | import android.widget.EditText;
55 | import android.widget.ImageView;
56 | import android.widget.ListView;
57 | import android.widget.TextView;
58 | import android.widget.Toast;
59 | import zhao.arsceditor.ResDecoder.ARSCCallBack;
60 | import zhao.arsceditor.ResDecoder.data.ResTable;
61 | import zhao.arsceditor.Translate.DoTranslate;
62 |
63 | @SuppressWarnings("deprecation")
64 | public class MainActivity extends Activity implements OnItemLongClickListener {
65 |
66 | // 存储字符串的集合
67 | public List txtOriginal = new ArrayList();
68 | // 存储修改后的字符串的集合
69 | public List txtTranslated = new ArrayList();
70 | // 存储字符串在资源中对应的键
71 | public List txtTranslated_Key = new ArrayList();
72 | // 列表控件
73 | public ListView stringListView;
74 | // 数据处理器
75 | public stringListAdapter mAdapter;
76 | // 存储资源Configs的集合
77 | public static List Configs;
78 | // 存储资源种类的集合
79 | public static List Types;
80 | // 存储资源的集合
81 | private List RESOURCES = new ArrayList();
82 | // 显示资源种类的文本控件
83 | private TextView textCategory;
84 | // 显示资源Config的文本框
85 | private TextView textConfig;
86 | // 用于在列表顶部显示信息的TextView控件
87 | private TextView info;
88 | // 翻译按钮
89 | private ImageView btnTranslate;
90 | // 搜索按钮
91 | private ImageView btnSearch;
92 | // 保存按钮
93 | private ImageView btnSave;
94 | // ARSC解析器
95 | private AndrolibResources mAndRes;
96 | // 字符串是否修改
97 | public boolean isChanged = false;
98 | // 资源类型
99 | private int ResType;
100 | // 资源类型常量
101 | public static final int ARSC = 0, AXML = 1, DEX = 2;
102 |
103 | @Override
104 | protected void onCreate(Bundle savedInstanceState) {
105 | super.onCreate(savedInstanceState);
106 | // 设置主界面布局文件
107 | setContentView(R.layout.string_list);
108 | // 初始化列表控件
109 | stringListView = (ListView) findViewById(R.id.list_res_string);
110 | // 初始化显示资源类型的文本框
111 | textCategory = (TextView) findViewById(R.id.textCategory);
112 | // 初始化显示资源Config的文本框
113 | textConfig = (TextView) findViewById(R.id.textConfig);
114 | // 初始化翻译按钮
115 | btnTranslate = (ImageView) findViewById(R.id.btnTranslate);
116 | // 初始化搜索按钮
117 | btnSearch = (ImageView) findViewById(R.id.btnSearch);
118 | // 初始化保存按钮
119 | btnSave = (ImageView) findViewById(R.id.btnSave);
120 | // 获取用来显示信息的文本框
121 | info = (TextView) findViewById(R.id.info);
122 | // 为显示资源类型的文本框设置点击事件的监听器
123 | textCategory.setOnClickListener(MyOnClickListener);
124 | // 为显示资源Config的文本框设置点击事件的监听器
125 | textConfig.setOnClickListener(MyOnClickListener);
126 | // 为显示资源类型的文本框设置文本内容改变的监听器
127 | textCategory.addTextChangedListener(textWatcher);
128 | // 为显示资源Config的文本框设置文本内容改变的监听器
129 | textConfig.addTextChangedListener(textWatcher);
130 | // 为翻译按钮设置点击事件监听器
131 | btnTranslate.setOnClickListener(MyOnClickListener);
132 | // 为搜索按钮设置点击事件监听器
133 | btnSearch.setOnClickListener(MyOnClickListener);
134 | // 为保存按钮设置点击事件监听器
135 | btnSave.setOnClickListener(MyOnClickListener);
136 | // 初始化数据适配器
137 | mAdapter = new stringListAdapter(this);
138 | // 为列表控件设置数据适配器
139 | stringListView.setAdapter(mAdapter);
140 | // 为列表控件设置长按事件监听器
141 | stringListView.setOnItemLongClickListener(this);
142 | try {
143 | open("/sdcard/resources.arsc");
144 | } catch (IOException e) {
145 | showMessage(this, e.toString()).show();
146 | }
147 | }
148 |
149 | // 列表项目长按事件处理
150 | @Override
151 | public boolean onItemLongClick(AdapterView> arg0, View arg1, final int arg2, long arg3) {
152 |
153 | if (textCategory.getText().toString().equals("id")) {
154 | return false;
155 | }
156 | // 弹出一个对话框,显示一个翻译选项
157 | new AlertDialog.Builder(this).setItems(R.array.translate, new DialogInterface.OnClickListener() {
158 | @Override
159 | public void onClick(DialogInterface arg0, int arg1) {
160 | // 初始化翻译
161 | DoTranslate translateTask = new DoTranslate(txtOriginal, txtTranslated, false, MainActivity.this);
162 | // 开启翻译
163 | translateTask.init(arg2);
164 | }
165 | }).create().show();
166 |
167 | return true;
168 | }
169 |
170 | // 一些控件的点击事件监听器
171 | private OnClickListener MyOnClickListener = new OnClickListener() {
172 | @Override
173 | public void onClick(View arg0) {
174 | // TODO Auto-generated method stub
175 | switch (arg0.getId()) {
176 | // 点击了翻译按钮
177 | case R.id.btnTranslate:
178 | if (textCategory.getText().toString().equals("id")) {
179 | Toast.makeText(MainActivity.this, R.string.can_not_edit, Toast.LENGTH_LONG).show();
180 | return;
181 | }
182 | // 初始化翻译
183 | DoTranslate translate = new DoTranslate(txtOriginal, txtTranslated, true, MainActivity.this);
184 | // 开启翻译·
185 | translate.init(0);
186 | break;
187 | // 点击了搜索按钮
188 | case R.id.btnSearch:
189 | // 初始化搜索
190 | SearchString searchTask = new SearchString(MainActivity.this, txtOriginal);
191 | // 开启搜索
192 | searchTask.search();
193 | break;
194 | // 点击了保存按钮
195 | case R.id.btnSave:
196 | // 创建一个线程用来保存资源文件
197 | SaveFileTask saveTask = new SaveFileTask();
198 | // 执行该线程
199 | saveTask.execute("/sdcard/1.arsc");
200 | break;
201 | // 点击了资源类型的文本框
202 | case R.id.textCategory:
203 | // 弹出一个对话框,列出所有的资源类型
204 | new AlertDialog.Builder(MainActivity.this).setTitle("")
205 | .setItems((String[]) Types.toArray(new String[Types.size()]),
206 | new DialogInterface.OnClickListener() {
207 | // 对话框上的条目点击的事件监听器
208 | @Override
209 | public void onClick(DialogInterface arg0, int arg1) {
210 | // TODO Auto-generated method stub
211 | textCategory.setText(Types.get(arg1));
212 | }
213 | })
214 | .create().show();
215 | break;
216 | // 点击了资源Config的文本框
217 | case R.id.textConfig:
218 | // 弹出一个对话框,列出所有的资源Config
219 | new AlertDialog.Builder(MainActivity.this).setTitle("")
220 | .setItems((String[]) Configs.toArray(new String[Configs.size()]),
221 | new DialogInterface.OnClickListener() {
222 | // 对话框上的条目点击的事件监听器
223 | @Override
224 | public void onClick(DialogInterface arg0, int arg1) {
225 | // TODO Auto-generated method stub
226 | textConfig.setText(Configs.get(arg1));
227 | }
228 | })
229 | .create().show();
230 | break;
231 | }
232 | }
233 | };
234 |
235 | private void open(String resFile) throws IOException {
236 | if (resFile.endsWith(".arsc")) {
237 | open(new FileInputStream(resFile), ARSC);
238 | } else if (resFile.endsWith(".xml")) {
239 | open(new FileInputStream(resFile), AXML);
240 | } else if (resFile.endsWith(".dex")) {
241 | open(new FileInputStream(resFile), DEX);
242 | } else {
243 | throw new IOException("Unsupported FileType");
244 | }
245 | }
246 |
247 | private void open(InputStream resInputStream, int resType) {
248 | // 初始化一个线程用来解析资源文件
249 | AsyncTask task = new ParseTask();
250 | try {
251 | // 开启该线程
252 | task.execute(resInputStream);
253 | ResType = resType;
254 | } catch (OutOfMemoryError e) {
255 | showMessage(this, getString(R.string.out_of_memory)).show();
256 | }
257 | // 初始化一个线程用来获取解析后的资源
258 | AsyncTask getTask = new GetTask();
259 | // 开启该线程
260 | getTask.execute(textCategory.getText().toString(), textConfig.getText().toString());
261 | }
262 |
263 | /**
264 | * 文本框内容改变的事件监听器
265 | *
266 | * @author zhaohai
267 | */
268 | private TextWatcher textWatcher = new TextWatcher() {
269 |
270 | // 文本改变后的事件处理
271 | @Override
272 | public void afterTextChanged(Editable s) {
273 | // 初始化一个线程用来获取资源
274 | AsyncTask task = new GetTask();
275 | // 开启该线程
276 | task.execute(textCategory.getText().toString(), textConfig.getText().toString());
277 | }
278 |
279 | // 文本改变之前的事件处理
280 | @Override
281 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
282 | // TODO Auto-generated method stub
283 | Log.d("TAG", "beforeTextChanged--------------->");
284 | }
285 |
286 | // 文本改变的事件处理
287 | @Override
288 | public void onTextChanged(CharSequence s, int start, int before, int count) {
289 |
290 | }
291 |
292 | };
293 |
294 | /**
295 | * 一个用来获取解析后的资源的线程
296 | *
297 | * @author zhaohai
298 | */
299 | class GetTask extends AsyncTask {
300 | // 进度条
301 | private ProgressDialog dlg;
302 |
303 | // 耗时任务开始前执行的任务
304 | @Override
305 | protected void onPreExecute() {
306 | super.onPreExecute();
307 | // 初始化进度条
308 | dlg = new ProgressDialog(MainActivity.this);
309 | // 设置标题
310 | dlg.setTitle(R.string.parsing);
311 | // 设置按返回进度条不消失
312 | dlg.setCancelable(false);
313 | // 显示进度条
314 | dlg.show();
315 |
316 | // 如果储存Config的列表未初始化
317 | if (Configs == null) {
318 | // 初始化Config列表
319 | Configs = new ArrayList();
320 | }
321 |
322 | // 检查是否发生改变
323 | for (String str : txtTranslated) {
324 | if (!str.equals(""))
325 | isChanged = true;
326 | break;
327 | }
328 |
329 | if (isChanged) {
330 | // 排序整理修改后的内容,以方便一一写入
331 | for (int i = 0; i < txtOriginal.size(); i++)
332 | mAndRes.mARSCDecoder.mTableStrings.sortStringBlock(txtOriginal.get(i), txtTranslated.get(i));
333 | }
334 |
335 | // 清除几个列表中的元素
336 | txtOriginal.clear();
337 | txtTranslated.clear();
338 | txtTranslated_Key.clear();
339 | Configs.clear();
340 | }
341 |
342 | // 执行耗时任务
343 | @Override
344 | protected String doInBackground(String... params) {
345 | switch (ResType) {
346 |
347 | case ARSC:
348 | for (ContentValues resource : RESOURCES) {
349 | // 获取资源的键
350 | String NAME = (String) resource.get(MyObj.NAME);
351 | // 获取资源的值
352 | String VALUE = (String) resource.get(MyObj.VALUE);
353 | // 获取资源类型
354 | String TYPE = (String) resource.get(MyObj.TYPE);
355 | // 获取资源分支
356 | String CONFIG = (String) resource.get(MyObj.CONFIG);
357 |
358 | // 如果资源的Config开头存在-符号,并且Config列表中不存在该资源的Config元素,并且资源种类是params[0]的值
359 | if (CONFIG.startsWith("-") && !Configs.contains(CONFIG.substring(1)) && TYPE.equals(params[0]))
360 | // 向Config列表中添加元素
361 | Configs.add(CONFIG.substring(1));
362 | // 如果资源的Config开头不存在-符号,并且Config列表中不存在该资源的Config元素,并且资源种类是params[0]的值
363 | else if (!CONFIG.startsWith("-") && !Configs.contains(CONFIG) && TYPE.equals(params[0]))
364 | Configs.add(CONFIG);
365 |
366 | // 如果资源的Config开头存在-符号,并且Config列表中存在该资源的Config元素,并且Config是params[1]的值
367 | if (TYPE.equals(params[0]) && CONFIG.startsWith("-") && CONFIG.substring(1).equals(params[1])) {
368 | // 向储存字符串的列表中添加字符串成员
369 | txtOriginal.add(VALUE);
370 | // 向储存修改后的字符串的列表中添加空成员
371 | txtTranslated.add("");
372 | // 向储存资源的键的列表添加键
373 | txtTranslated_Key.add(NAME);
374 | // 如果资源的Config开头不存在-符号,并且Config列表中存在该资源的Config元素,并且Config是params[1]的值
375 | } else if (TYPE.equals(params[0]) && !CONFIG.startsWith("-") && CONFIG.equals(params[1])) {
376 | // 向储存字符串的列表中添加字符串成员
377 | txtOriginal.add(VALUE);
378 | // 向储存修改后的字符串的列表中添加空成员
379 | txtTranslated.add("");
380 | // 向储存资源的键的列表添加键
381 | txtTranslated_Key.add(NAME);
382 | }
383 | }
384 | break;
385 | case AXML:
386 | try {
387 | mAndRes.mAXMLDecoder.getStrings(txtOriginal);
388 | for (int i = 0; i < txtOriginal.size(); i++) {
389 | // 向储存修改后的字符串的列表中添加空成员
390 | txtTranslated.add("");
391 | // 向储存资源的键添加空成员
392 | txtTranslated_Key.add("");
393 | }
394 | } catch (CharacterCodingException e) {
395 | return e.toString();
396 | }
397 | break;
398 | case DEX:
399 |
400 | break;
401 | }
402 | // 返回一个成功的标志
403 | return getString(R.string.success);
404 | }
405 |
406 | // 耗时任务执行完毕后的事件处理
407 | @Override
408 | protected void onPostExecute(String result) {
409 | // 隐藏进度条
410 | dlg.dismiss();
411 | // 如果收到的返回值不是成功的标志
412 | if (!result.equals(getString(R.string.success))) {
413 | // 则显示错误详情
414 | showMessage(MainActivity.this, result).show();
415 | return;
416 | } else if (result.equals(getString(R.string.success)) && txtOriginal.size() == 0) // 如果收到成功的标志,并且字符串列表没有成员,说明资源列表中不存在这样的成员
417 | {
418 | if (Configs.size() != 0)
419 | // 那么就获取默认的资源
420 | textConfig.setText(
421 | Configs.contains("[DEFAULT]") ? Configs.get(Configs.indexOf("[DEFAULT]")) : Configs.get(0));
422 | }
423 | // 对Config列表进行排序
424 | Collections.sort(Configs);
425 | //
426 | // Collections.sort(txtOriginal);
427 | // 通知数据适配器更新数据
428 | mAdapter.notifyDataSetInvalidated();
429 | }
430 |
431 | }
432 |
433 | // 一个储存键的类
434 | class MyObj {
435 | public final static String NAME = "name";
436 | public final static String VALUE = "value";
437 | public final static String TYPE = "type";
438 | public final static String CONFIG = "config";
439 | }
440 |
441 | /**
442 | * @author zhaohai 一个用来解析ARSC的线程
443 | */
444 | class ParseTask extends AsyncTask {
445 | // 资源回调接口
446 | private ARSCCallBack callback;
447 | // 进度条
448 | private ProgressDialog dlg;
449 | // 创建values值对象
450 | private ContentValues values = null;
451 |
452 | // 耗时任务开始前执行的任务
453 | @Override
454 | protected void onPreExecute() {
455 | super.onPreExecute();
456 | // 初始化ARSC解析器
457 | mAndRes = new AndrolibResources(MainActivity.this);
458 | // 初始化进度条
459 | dlg = new ProgressDialog(MainActivity.this);
460 | // 设置进度条标题
461 | dlg.setTitle(R.string.parsing);
462 | // 设置按进度条外部进度条不消失
463 | dlg.setCancelable(false);
464 | // 显示进度条
465 | dlg.show();
466 |
467 | // 如果储存资源类型的列表未初始化
468 | if (Types == null) {
469 | // 初始化储存资源类型的列表
470 | Types = new ArrayList();
471 | }
472 |
473 | // 实现资源回调接口
474 | callback = new ARSCCallBack() {
475 | // 进度值
476 | int i = 0;
477 |
478 | @Override
479 | public void back(String config, String type, String key, String value) {
480 | // 这里是为了出去一些不能编辑的字符串
481 | if (type != null) {
482 | // 初始化键值映射
483 | values = new ContentValues();
484 | // 向映射中添加资源的键
485 | values.put(MyObj.NAME, key);
486 | // 向映射中添加资源的值
487 | values.put(MyObj.VALUE, value);
488 | // 向映射中添加资源的种类
489 | values.put(MyObj.TYPE, type);
490 | // 向映射中添加资源的Config
491 | values.put(MyObj.CONFIG, config);
492 | // 向资源中添加该映射
493 | RESOURCES.add(values);
494 | }
495 | // 如果资源种类集合中不存在该种类
496 | if (!Types.contains(type))
497 | // 向其中添加该种类
498 | Types.add(type);
499 | // 进度值加1
500 | i++;
501 | // 更新进度条
502 | publishProgress(i);
503 | }
504 | };
505 | }
506 |
507 | // 获取ARSC文件的ResTable的方法
508 | public ResTable getResTable(InputStream ARSCStream) throws IOException {
509 | return mAndRes.getResTable(ARSCStream);
510 | }
511 |
512 | // 执行耗时任务
513 | @Override
514 | protected String doInBackground(InputStream... params) {
515 | try {
516 | switch (ResType) {
517 | case ARSC:
518 | // 解析ARSC
519 | mAndRes.decodeARSC(getResTable(params[0]), callback);
520 | break;
521 | case AXML:
522 | // 解析ARSC
523 | mAndRes.decodeAXML(params[0]);
524 | break;
525 | }
526 | } catch (Exception e) {
527 | return e.toString();
528 | }
529 | return getString(R.string.success);
530 | }
531 |
532 | // 更新ui界面
533 | @Override
534 | protected void onProgressUpdate(Integer... values) {
535 | dlg.setMessage(String.valueOf(values[0]));
536 | }
537 |
538 | // 耗时任务执行完毕后的事件处理
539 | @Override
540 | protected void onPostExecute(String result) {
541 | // 隐藏进度条
542 | dlg.dismiss();
543 | // 如果返回的结果不是成功
544 | if (!result.equals(getString(R.string.success))) {
545 | // 显示错误信息
546 | showMessage(MainActivity.this, result).show();
547 | return;
548 | }
549 | // 对资源种类列表排序
550 | Collections.sort(Types);
551 | }
552 |
553 | }
554 |
555 | /**
556 | * @author zhaohai 一个用来保存资源文件的线程
557 | */
558 | class SaveFileTask extends AsyncTask {
559 | // 进度条
560 | private ProgressDialog dlg;
561 |
562 | // 耗时任务开始前执行的任务
563 | @Override
564 | protected void onPreExecute() {
565 | super.onPreExecute();
566 | // 初始化进度条
567 | dlg = new ProgressDialog(MainActivity.this);
568 | // 设置进度条标题
569 | dlg.setTitle(R.string.saving);
570 | // 设置按进度条外部进度条不消失
571 | dlg.setCancelable(false);
572 | // 显示进度条
573 | dlg.show();
574 | }
575 |
576 | // 执行耗时任务
577 | @Override
578 | protected String doInBackground(String... params) {
579 | try {
580 | switch (ResType) {
581 | case ARSC:
582 | // 创建新文件输出流
583 | FileOutputStream fo1 = new FileOutputStream(params[0]);
584 | mAndRes.mARSCDecoder.write(fo1, txtOriginal, txtTranslated);
585 | fo1.close();
586 | break;
587 | case AXML:
588 | // 创建新文件输出流
589 | FileOutputStream fo2 = new FileOutputStream(params[0]);
590 | mAndRes.mAXMLDecoder.write(txtOriginal, txtTranslated, fo2);
591 | fo2.close();
592 | break;
593 | case DEX:
594 | break;
595 | }
596 | } catch (IOException e) {
597 | return e.toString();
598 | } catch (OutOfMemoryError e) {
599 | return getString(R.string.out_of_memory);
600 | }
601 | return getString(R.string.success);
602 | }
603 |
604 | // 耗时任务执行完毕后的事件处理
605 | @Override
606 | protected void onPostExecute(String result) {
607 | // 隐藏进度条
608 | dlg.dismiss();
609 | // 如果返回的结果不是成功
610 | if (!result.equals(getString(R.string.success))) {
611 | // 显示错误信息
612 | showMessage(MainActivity.this, result).show();
613 | return;
614 | }
615 | // 还原是否改变的标志
616 | isChanged = false;
617 | }
618 |
619 | }
620 |
621 | // 数据适配器
622 | public class stringListAdapter extends BaseAdapter {
623 |
624 | // 上下文
625 | private Context mContext;
626 | // 显示原来的字符串内容的控件
627 | private TextView txtOriginalView;
628 | // 用于修改的文本框控件
629 | private EditText txtTranslatedView;
630 |
631 | // 构造函数
632 | public stringListAdapter(Context context) {
633 | super();
634 | // 获取上下文
635 | this.mContext = context;
636 | }
637 |
638 | // 获取数据成员个数
639 | @Override
640 | public int getCount() {
641 | // TODO Auto-generated method stub
642 | return txtOriginal.size();
643 | }
644 |
645 | // 获取指定条目的内容
646 | @Override
647 | public Object getItem(int arg0) {
648 | // TODO Auto-generated method stub
649 | return arg0;
650 | }
651 |
652 | // 获取指定条目的文字
653 | @Override
654 | public long getItemId(int arg0) {
655 | // TODO Auto-generated method stub
656 | return arg0;
657 | }
658 |
659 | // 文本框点击是事件监听器
660 | OnTouchListener touch = new OnTouchListener() {
661 | @SuppressLint("ClickableViewAccessibility")
662 | @Override
663 | public boolean onTouch(View arg0, MotionEvent arg1) {
664 | if (arg1.getAction() == MotionEvent.ACTION_UP)
665 | Toast.makeText(MainActivity.this, R.string.can_not_edit, Toast.LENGTH_LONG).show();
666 | return false;
667 | }
668 | };
669 |
670 | // 获取View
671 | @SuppressLint({ "ViewHolder", "InflateParams" })
672 | @Override
673 | public View getView(final int position, View view, ViewGroup arg2) {
674 |
675 | // 文本框内容改变的事件监听器
676 | TextWatcher textWatcher = new TextWatcher() {
677 |
678 | // 文本改变后的事件处理
679 | @Override
680 | public void afterTextChanged(Editable s) {
681 |
682 | }
683 |
684 | // 文本改变之前的事件处理
685 | @Override
686 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
687 | // TODO Auto-generated method stub
688 | Log.d("TAG", "beforeTextChanged--------------->");
689 | }
690 |
691 | // 文本改变的事件处理
692 | @Override
693 | public void onTextChanged(CharSequence s, int start, int before, int count) {
694 | // 从集合中移除该条目对应的空白内容
695 | txtTranslated.remove(position);
696 | // 向当前位置添加新的内容,以此实现文本的更新
697 | txtTranslated.add(position, s.toString());
698 | }
699 |
700 | };
701 |
702 | // 创建view对象
703 | view = LayoutInflater.from(mContext).inflate(R.layout.res_string_item, null);
704 | // 获取显示原来的字符串的控件
705 | txtOriginalView = (TextView) view.findViewById(R.id.txtOriginal);
706 | // 获取用来修改的文本框
707 | txtTranslatedView = (EditText) view.findViewById(R.id.txtTranslated);
708 |
709 | // 如果选中了style资源,则显示没有可供编辑的字符串的提示
710 | if (textCategory.getText().toString().equals("style")) {
711 | // 隐藏列表
712 | stringListView.setVisibility(View.INVISIBLE);
713 | // 显示一个控件用来提示
714 | info.setVisibility(View.VISIBLE);
715 | // 显示文字
716 | info.setText(R.string.no_strings_for_editing);
717 | return view;
718 | } else {
719 | // 隐藏显示信息的控件
720 | info.setVisibility(View.GONE);
721 | // 显示列表
722 | stringListView.setVisibility(View.VISIBLE);
723 | }
724 | // 如果选中了id资源,则设置文本框不可编辑,因为id不能随意编辑
725 | if (textCategory.getText().toString().equals("id")) {
726 | txtTranslatedView.setFocusable(false);
727 | txtTranslatedView.setOnTouchListener(touch);
728 | }
729 | // 显示原来的字符串
730 | txtOriginalView.setText(txtOriginal.get(position));
731 | // 显示修改后的字符串
732 | txtTranslatedView.setText(txtTranslated.get(position));
733 | // 为文本框设置底层的显示内容
734 | txtTranslatedView.setHint(txtTranslated_Key.get(position));
735 | // 为文本框设置内容改变的监听器
736 | txtTranslatedView.addTextChangedListener(textWatcher);
737 | return view;
738 | }
739 | }
740 |
741 | // 显示信息的方法
742 | public static AlertDialog.Builder showMessage(Context activity, String message) {
743 | return new AlertDialog.Builder(activity).setMessage(message).setNegativeButton(R.string.ok, null)
744 | .setCancelable(false).setTitle(R.string.error);
745 | }
746 | }
747 |
--------------------------------------------------------------------------------