Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44fa23020e | ||
|
|
4cdcf9aee0 | ||
|
|
63c5262444 | ||
|
|
9074e80478 | ||
|
|
13fab86b83 | ||
|
|
5ada5d74d2 | ||
|
|
aa1802c371 | ||
|
|
0cf1c6ecba | ||
|
|
f051420ff9 | ||
|
|
3902a40e30 | ||
|
|
0c97f15088 | ||
|
|
4cf112921b | ||
|
|
6e31b26af0 | ||
|
|
6f22b5dd03 | ||
|
|
9ee1bd0f79 | ||
|
|
56b11bc43c | ||
|
|
a476d490a6 | ||
|
|
74b50fb9c2 | ||
|
|
ddaedd0ba0 | ||
|
|
729342085a | ||
|
|
bb20178dc9 | ||
|
|
306cee1fd4 | ||
|
|
02e8732e9f |
@@ -21,21 +21,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up NDK
|
|
||||||
uses: nttld/setup-ndk@v1
|
|
||||||
with:
|
|
||||||
ndk-version: r25c
|
|
||||||
add-to-path: false
|
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download tools
|
- name: Download tools
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
@@ -54,8 +47,6 @@ jobs:
|
|||||||
java -jar scripts/apktool.jar b smali -o smali/VTLite.apk --use-aapt2 -p scripts/framework/
|
java -jar scripts/apktool.jar b smali -o smali/VTLite.apk --use-aapt2 -p scripts/framework/
|
||||||
zipalign -p -f 4 smali/VTLite.apk smali/VTLite.apk_aligned
|
zipalign -p -f 4 smali/VTLite.apk smali/VTLite.apk_aligned
|
||||||
mv smali/VTLite.apk_aligned smali/VTLite.apk
|
mv smali/VTLite.apk_aligned smali/VTLite.apk
|
||||||
env:
|
|
||||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
|
||||||
|
|
||||||
- name: Sign and verify the apk
|
- name: Sign and verify the apk
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="RClass Regeneration" type="ShConfigurationType">
|
|
||||||
<option name="SCRIPT_TEXT" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
|
||||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/scripts/rclass_regenerator.sh" />
|
|
||||||
<option name="SCRIPT_OPTIONS" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
|
||||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration default="false" name="RClass Regeneration" type="BatchConfigurationType" factoryName="Batch">
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
|
||||||
<option name="PARENT_ENVS" value="true" />
|
|
||||||
<module name="" />
|
|
||||||
<option name="SCRIPT_NAME" value="./scripts/rclass_regenerator.bat" />
|
|
||||||
<option name="PARAMETERS" value="" />
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Update APKs Jars (MacOS)" type="ShConfigurationType">
|
|
||||||
<option name="SCRIPT_TEXT" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
|
||||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/scripts/update_jars_brew.sh" />
|
|
||||||
<option name="SCRIPT_OPTIONS" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
|
||||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Update APKs Jars" type="ShConfigurationType">
|
|
||||||
<option name="SCRIPT_TEXT" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
|
||||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/scripts/update_jars.sh" />
|
|
||||||
<option name="SCRIPT_OPTIONS" value="" />
|
|
||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
|
||||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
|
||||||
<option name="INTERPRETER_OPTIONS" value="" />
|
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
|
||||||
<envs />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndInstallVTLBeta]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndInstallVTLBeta" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndInstallVTLDev]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndInstallVTLDev" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndInstallVTLRelease]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndInstallVTLRelease" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndLaunchVTLBeta]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndLaunchVTLBeta" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndLaunchVTLDev]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndLaunchVTLDev" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildAndLaunchVTLRelease]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildAndLaunchVTLRelease" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildVTLBeta]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildVTLBeta" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildVTLDev]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildVTLDev" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:buildVTLRelease]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:buildVTLRelease" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:exportDexBeta]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:exportDexBeta" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:exportDexDev]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:exportDexDev" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:exportDexRelease]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:exportDexRelease" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:prepareDexForMergeDev]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:prepareDexForMergeDev" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="lite [app:prepareDexForMergeRelease]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="app:prepareDexForMergeRelease" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="-Xms4g -Xmx4g" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
+37
-36
@@ -20,9 +20,9 @@ cargo {
|
|||||||
def smaliFolder = projectDir.toPath().resolve("../smali").normalize()
|
def smaliFolder = projectDir.toPath().resolve("../smali").normalize()
|
||||||
def scriptsFolder = projectDir.toPath().resolve("../scripts").normalize()
|
def scriptsFolder = projectDir.toPath().resolve("../scripts").normalize()
|
||||||
//you can add own folders with decompiled smali code
|
//you can add own folders with decompiled smali code
|
||||||
def dexes = smaliFolder.toFile()
|
def dexes=smaliFolder.toFile()
|
||||||
.listFiles({ f -> f.isDirectory() && f.name.startsWith("smali") } as FileFilter)
|
.listFiles( { f -> f.isDirectory() && f.name.startsWith("smali") } as FileFilter)
|
||||||
.size()
|
.size()
|
||||||
|
|
||||||
versionFile {
|
versionFile {
|
||||||
file = projectDir.toPath().resolve("src/main/assets/version.properties").toFile()
|
file = projectDir.toPath().resolve("src/main/assets/version.properties").toFile()
|
||||||
@@ -91,13 +91,11 @@ task testClasses {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.4'
|
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.1'
|
||||||
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.4'
|
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.1'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||||
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
|
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
|
||||||
implementation 'com.google.guava:guava:32.0.0-android'
|
implementation 'com.google.guava:guava:23.3-android'
|
||||||
|
|
||||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||||
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
|
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
@@ -107,35 +105,38 @@ task generateBuildTasks() {
|
|||||||
android.applicationVariants.all { variant ->
|
android.applicationVariants.all { variant ->
|
||||||
variant.outputs.each { output ->
|
variant.outputs.each { output ->
|
||||||
def buildType = output.name.toString().capitalize()
|
def buildType = output.name.toString().capitalize()
|
||||||
if (buildType == "Base") return
|
if(buildType=="Base")return
|
||||||
task "exportDex$buildType"() {
|
task "exportDex$buildType"() {
|
||||||
dependsOn('assemble' + buildType)
|
dependsOn('assemble' + buildType)
|
||||||
doLast {
|
doLast {
|
||||||
try {
|
try {
|
||||||
def suffix = buildType.toLowerCase()
|
def suffix=buildType.toLowerCase()
|
||||||
String apkPath = projectDir.getAbsolutePath() + "/build/outputs/apk/$suffix/app-$suffix"
|
String apkPath=projectDir.getAbsolutePath()+"/build/outputs/apk/$suffix/app-$suffix"
|
||||||
def apk = new File(apkPath + "-unsigned.apk")
|
def apk=new File(apkPath+"-unsigned.apk")
|
||||||
if (!apk.exists()) apk = new File(apkPath + ".apk")
|
if(!apk.exists())apk=new File(apkPath+".apk")
|
||||||
def zip = new ZipFile(apk)
|
def zip=new ZipFile(apk)
|
||||||
def dexCounter = dexes
|
def entries=zip.entries()
|
||||||
zip.stream()
|
def dexCounter=dexes
|
||||||
.filter(entry -> entry.name.matches("^classes(?:(?=\\d)2|)\\.dex\$") || entry.name == "assets/version.properties")
|
while(entries.hasMoreElements())
|
||||||
.forEach(entry -> {
|
{
|
||||||
String filename
|
def entry=entries.nextElement()
|
||||||
if (entry.name.matches("^classes(?:(?=\\d)2|)\\.dex\$")) filename = "classes${++dexCounter}.dex"
|
String filename
|
||||||
else filename = "version.properties"
|
if(entry.name.matches("^classes(?:(?=\\d)2|)\\.dex\$"))filename="classes${++dexCounter}.dex"
|
||||||
println("Exporting " + entry.name)
|
else if(entry.name=="assets/version.properties")filename="version.properties"
|
||||||
def tmpFile = new File(projectDir, filename)
|
else continue
|
||||||
try (def is = zip.getInputStream(entry); def fos = new FileOutputStream(tmpFile)) {
|
println("Exporting "+entry.name)
|
||||||
def buff = new byte[0x800]
|
def tmpFile=new File(projectDir,filename)
|
||||||
int len
|
def is=zip.getInputStream(entry)
|
||||||
while ((len = is.read(buff)) > 0) fos.write(buff, 0, len)
|
def fos=new FileOutputStream(tmpFile)
|
||||||
}
|
def buff=new byte[0x800]
|
||||||
});
|
int len
|
||||||
|
while((len=is.read(buff))>0)fos.write(buff,0,len)
|
||||||
|
is.close()
|
||||||
|
fos.close()
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
task "prepareDexForMerge$buildType"(type: Copy) {
|
task "prepareDexForMerge$buildType"(type: Copy) {
|
||||||
@@ -151,19 +152,19 @@ task generateBuildTasks() {
|
|||||||
include 'version.properties'
|
include 'version.properties'
|
||||||
into 'assets'
|
into 'assets'
|
||||||
}
|
}
|
||||||
from('../native-effects/target/aarch64-linux-android/release') {
|
from('../native-effects/target/aarch64-linux-android/release'){
|
||||||
include 'libnative_effects.so'
|
include 'libnative_effects.so'
|
||||||
into 'lib/arm64-v8a'
|
into 'lib/arm64-v8a'
|
||||||
}
|
}
|
||||||
from('../native-effects/target/armv7-linux-androideabi/release') {
|
from('../native-effects/target/armv7-linux-androideabi/release'){
|
||||||
include 'libnative_effects.so'
|
include 'libnative_effects.so'
|
||||||
into 'lib/armeabi-v7a'
|
into 'lib/armeabi-v7a'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
task "cleanTemporaryFiles$buildType"(type: Delete)
|
task "cleanTemporaryFiles$buildType"(type:Delete)
|
||||||
{
|
{
|
||||||
delete fileTree(dir: projectDir, include: ["*.dex", "version.properties"])
|
delete fileTree(dir:projectDir,include:["*.dex","version.properties"])
|
||||||
delete fileTree(dir: smaliFolder, include: ['*.dex', "assets/version.properties"])
|
delete fileTree(dir:smaliFolder,include:['*.dex',"assets/version.properties"])
|
||||||
}
|
}
|
||||||
task "buildVTL$buildType"(type: Exec) {
|
task "buildVTL$buildType"(type: Exec) {
|
||||||
dependsOn('prepareDexForMerge' + buildType)
|
dependsOn('prepareDexForMerge' + buildType)
|
||||||
|
|||||||
Binary file not shown.
Vendored
+3
-2
@@ -35,7 +35,7 @@
|
|||||||
-optimizationpasses 5
|
-optimizationpasses 5
|
||||||
-keepattributes SourceFile, LineNumberTable
|
-keepattributes SourceFile, LineNumberTable
|
||||||
|
|
||||||
-keep,allowoptimization class ru.vtosters.hooks.**, ru.vtosters.sponsorpost.internal.Native, ru.vtosters.lite.utils.AndroidUtils, ru.vtosters.lite.utils.NetworkUtils, ru.vtosters.lite.utils.CrashReporter, ru.vtosters.lite.ui.activities.**, ru.vtosters.lite.services.**, ru.vtosters.lite.ui.fragments.**, ru.vtosters.lite.themes.**, ru.vtosters.lite.music.hook.**, ru.vtosters.lite.music.cache.injectors.**, ru.vtosters.lite.ui.wallpapers.NativeEffects, com.aefyr.tsg.g2.TelegramStickersService {
|
-keep,allowoptimization class ru.vtosters.hooks.**, ru.vtosters.lite.utils.AndroidUtils, ru.vtosters.lite.utils.NetworkUtils, ru.vtosters.lite.utils.CrashReporter, ru.vtosters.lite.ui.activities.**, ru.vtosters.lite.services.**, ru.vtosters.lite.ui.fragments.**, ru.vtosters.lite.themes.**, ru.vtosters.lite.music.hook.**, ru.vtosters.lite.music.cache.injectors.**, ru.vtosters.lite.ui.wallpapers.NativeEffects, com.aefyr.tsg.g2.TelegramStickersService {
|
||||||
public <fields>;
|
public <fields>;
|
||||||
public <methods>;
|
public <methods>;
|
||||||
public <init>(...);
|
public <init>(...);
|
||||||
@@ -43,4 +43,5 @@
|
|||||||
|
|
||||||
-keep class com.vtosters.lite.** { *; }
|
-keep class com.vtosters.lite.** { *; }
|
||||||
-keep class com.vk.** { *; }
|
-keep class com.vk.** { *; }
|
||||||
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
|
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
|
||||||
|
-keep class com.guardanis.applock.** { *; }
|
||||||
@@ -6,7 +6,6 @@ import android.os.RemoteException;
|
|||||||
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
import com.vk.music.common.MusicPlaybackLaunchContext;
|
import com.vk.music.common.MusicPlaybackLaunchContext;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -34,7 +33,7 @@ public class LibVKXClient {
|
|||||||
public static boolean isVkxInstalled() {
|
public static boolean isVkxInstalled() {
|
||||||
try {
|
try {
|
||||||
getGlobalContext().getPackageManager().getPackageInfo("ua.itaysonlab.vkx", 0);
|
getGlobalContext().getPackageManager().getPackageInfo("ua.itaysonlab.vkx", 0);
|
||||||
return isValidSignature() && !Preferences.serverFeaturesDisable();
|
return isValidSignature();
|
||||||
} catch (Exception unused) {
|
} catch (Exception unused) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -81,7 +80,6 @@ public class LibVKXClient {
|
|||||||
lambdaplay(list, musicTrack, iLibVkxService, null);
|
lambdaplay(list, musicTrack, iLibVkxService, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String asId(MusicTrack musicTrack) {
|
public static String asId(MusicTrack musicTrack)
|
||||||
return musicTrack.y1();
|
{ return musicTrack.y1(); }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -36,8 +36,8 @@ public class TelegramStickersPack extends CustomStickersPack {
|
|||||||
public HashMap<String, List<Integer>> emojis = new HashMap<>();
|
public HashMap<String, List<Integer>> emojis = new HashMap<>();
|
||||||
|
|
||||||
public TelegramStickersPack(String id) {
|
public TelegramStickersPack(String id) {
|
||||||
this.id = this.title = id;
|
this.id=this.title=id;
|
||||||
this.hash = id.hashCode();
|
this.hash=id.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject encodeEmojis() throws JSONException {
|
public JSONObject encodeEmojis() throws JSONException {
|
||||||
|
|||||||
@@ -26,13 +26,19 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
|||||||
public class TelegramStickersService {
|
public class TelegramStickersService {
|
||||||
private static final String TAG = "TGStickersService";
|
private static final String TAG = "TGStickersService";
|
||||||
private static TelegramStickersService instance;
|
private static TelegramStickersService instance;
|
||||||
|
|
||||||
|
private final Context c;
|
||||||
|
|
||||||
private final ArrayList<TelegramStickersPack> packs;
|
private final ArrayList<TelegramStickersPack> packs;
|
||||||
private final ArrayList<TelegramStickersPack> activePacks;
|
private final ArrayList<TelegramStickersPack> activePacks;
|
||||||
private final ArrayList<TelegramStickersPack> inactivePacks;
|
private final ArrayList<TelegramStickersPack> inactivePacks;
|
||||||
private final ArrayList<StickersEventsListener> listeners;
|
private final ArrayList<StickersEventsListener> listeners;
|
||||||
private final HashSet<String> currentlyDownloading;
|
private final HashSet<String> currentlyDownloading;
|
||||||
|
|
||||||
private final Handler uiThreadHandler;
|
private final Handler uiThreadHandler;
|
||||||
|
|
||||||
private final TelegramStickersGrabber grabber;
|
private final TelegramStickersGrabber grabber;
|
||||||
|
|
||||||
private final TelegramStickersDbHelper dbHelper;
|
private final TelegramStickersDbHelper dbHelper;
|
||||||
private final ArrayList<Runnable> queuedTasks;
|
private final ArrayList<Runnable> queuedTasks;
|
||||||
private final ThreadPoolExecutor executor;
|
private final ThreadPoolExecutor executor;
|
||||||
@@ -42,14 +48,16 @@ public class TelegramStickersService {
|
|||||||
private TelegramStickersService(Context context) {
|
private TelegramStickersService(Context context) {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
|
this.c = context.getApplicationContext();
|
||||||
|
|
||||||
listeners = new ArrayList<>();
|
listeners = new ArrayList<>();
|
||||||
currentlyDownloading = new HashSet<>();
|
currentlyDownloading = new HashSet<>();
|
||||||
uiThreadHandler = new Handler(Looper.getMainLooper());
|
uiThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
grabber = new TelegramStickersGrabber(null);
|
grabber = new TelegramStickersGrabber(null);
|
||||||
dbHelper = new TelegramStickersDbHelper(context);
|
dbHelper = new TelegramStickersDbHelper(c);
|
||||||
queuedTasks = new ArrayList<>();
|
queuedTasks = new ArrayList<>();
|
||||||
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
|
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
|
||||||
notificationsHelper = new NotificationsHelper(context);
|
notificationsHelper = new NotificationsHelper(c);
|
||||||
|
|
||||||
packs = new ArrayList<>();
|
packs = new ArrayList<>();
|
||||||
activePacks = new ArrayList<>();
|
activePacks = new ArrayList<>();
|
||||||
@@ -62,11 +70,11 @@ public class TelegramStickersService {
|
|||||||
return instance == null ? new TelegramStickersService(c) : instance;
|
return instance == null ? new TelegramStickersService(c) : instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePacks(boolean notify) {
|
private void updatePacks(final boolean notify) {
|
||||||
packs.clear();
|
packs.clear();
|
||||||
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
|
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackLoaded(TelegramStickersPack pack) {
|
public void onPackLoaded(final TelegramStickersPack pack) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
packs.add(pack);
|
packs.add(pack);
|
||||||
|
|
||||||
@@ -95,6 +103,14 @@ public class TelegramStickersService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addStickersEventsListener(StickersEventsListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeStickersEventsListener(StickersEventsListener listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
|
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
|
||||||
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
|
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
|
||||||
if (listeners.isEmpty())
|
if (listeners.isEmpty())
|
||||||
@@ -172,16 +188,20 @@ public class TelegramStickersService {
|
|||||||
return inactivePacks;
|
return inactivePacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestPackDownload(String id, File packFolder) {
|
public boolean isDoneLoading() {
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requestPackDownload(final String id, File packFolder) {
|
||||||
if (currentlyDownloading.contains(id)) {
|
if (currentlyDownloading.contains(id)) {
|
||||||
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
|
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
File folder = packFolder;
|
final File folder = packFolder;
|
||||||
queuedTasks.add(() -> requestPackDownload(id, folder));
|
queuedTasks.add(() -> requestPackDownload(id, folder));
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TelegramStickersPack pack = new TelegramStickersPack(id);
|
TelegramStickersPack pack = new TelegramStickersPack(id);
|
||||||
@@ -201,86 +221,85 @@ public class TelegramStickersService {
|
|||||||
notifyPackAdded(pack, packs.size() - 1);
|
notifyPackAdded(pack, packs.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TelegramStickersPack newPack = pack;
|
final TelegramStickersPack newPack = pack;
|
||||||
boolean isUpdate = update;
|
final boolean isUpdate = update;
|
||||||
|
|
||||||
currentlyDownloading.add(id);
|
currentlyDownloading.add(id);
|
||||||
notificationsHelper.packStartedDownloading(newPack);
|
notificationsHelper.packStartedDownloading(newPack);
|
||||||
|
|
||||||
// try-catch block to handle possible timeout exception
|
|
||||||
try {
|
grabber.enableProxy();
|
||||||
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
|
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
|
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
|
||||||
runOnUiThread(() -> {
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
newPack.state = TelegramStickersPack.DOWNLOADED;
|
||||||
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
|
currentlyDownloading.remove(newPack.id);
|
||||||
|
syncPack(newPack);
|
||||||
|
|
||||||
|
if (isUpdate) {
|
||||||
|
if (newPack.enabled)
|
||||||
|
notifyActivePacksListChanged();
|
||||||
|
} else {
|
||||||
|
activePacks.add(newPack);
|
||||||
|
notifyActivePacksListChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPackDownloadError(final Exception e) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
Log.e(TAG, "Error while downloading pack " + newPack.id);
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
if (!isUpdate) {
|
||||||
|
int index = packs.indexOf(newPack);
|
||||||
|
packs.remove(index);
|
||||||
|
notifyPackRemoved(newPack, index);
|
||||||
|
} else {
|
||||||
newPack.state = TelegramStickersPack.DOWNLOADED;
|
newPack.state = TelegramStickersPack.DOWNLOADED;
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
currentlyDownloading.remove(newPack.id);
|
}
|
||||||
syncPack(newPack);
|
|
||||||
|
|
||||||
if (isUpdate) {
|
notifyPackDownloadError(newPack, e);
|
||||||
if (newPack.enabled)
|
currentlyDownloading.remove(newPack.id);
|
||||||
notifyActivePacksListChanged();
|
});
|
||||||
} else {
|
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
||||||
activePacks.add(newPack);
|
}
|
||||||
notifyActivePacksListChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPackDownloadError(Exception e) {
|
public void onGotPackInfo(final TelegramStickersPackInfo packInfo) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
Log.e(TAG, "Error while downloading pack " + newPack.id);
|
newPack.title = packInfo.title;
|
||||||
e.printStackTrace();
|
newPack.stickersCount = packInfo.stickersCount;
|
||||||
|
newPack.version = packInfo.version;
|
||||||
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
|
});
|
||||||
|
|
||||||
if (!isUpdate) {
|
notificationsHelper.packDownloadUpdated(newPack, 0);
|
||||||
int index = packs.indexOf(newPack);
|
}
|
||||||
packs.remove(index);
|
|
||||||
notifyPackRemoved(newPack, index);
|
|
||||||
} else {
|
|
||||||
newPack.state = TelegramStickersPack.DOWNLOADED;
|
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyPackDownloadError(newPack, e);
|
@Override
|
||||||
currentlyDownloading.remove(newPack.id);
|
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
|
||||||
});
|
stickerIndex--;
|
||||||
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
List<Integer> list = newPack.emojis.get(boundEmoji);
|
||||||
public void onGotPackInfo(TelegramStickersPackInfo packInfo) {
|
if (list == null) list = new ArrayList<>();
|
||||||
runOnUiThread(() -> {
|
list.add(stickerIndex);
|
||||||
newPack.title = packInfo.title;
|
newPack.emojis.put(boundEmoji, list);
|
||||||
newPack.stickersCount = packInfo.stickersCount;
|
|
||||||
newPack.version = packInfo.version;
|
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
|
||||||
});
|
|
||||||
|
|
||||||
notificationsHelper.packDownloadUpdated(newPack, 0);
|
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
return true;
|
||||||
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
|
|
||||||
stickerIndex--;
|
|
||||||
|
|
||||||
List<Integer> list = newPack.emojis.get(boundEmoji);
|
|
||||||
if (list == null) list = new ArrayList<>();
|
|
||||||
list.add(stickerIndex);
|
|
||||||
newPack.emojis.put(boundEmoji, list);
|
|
||||||
|
|
||||||
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
// handle other possible exceptions
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPackEnabled(TelegramStickersPack pack, boolean enabled, boolean notify) {
|
public void setPackEnabled(final TelegramStickersPack pack, final boolean enabled, final boolean notify) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
|
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
|
||||||
return;
|
return;
|
||||||
@@ -306,7 +325,7 @@ public class TelegramStickersService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePack(TelegramStickersPack pack) {
|
public void deletePack(final TelegramStickersPack pack) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> deletePack(pack));
|
queuedTasks.add(() -> deletePack(pack));
|
||||||
return;
|
return;
|
||||||
@@ -315,6 +334,7 @@ public class TelegramStickersService {
|
|||||||
boolean d = dbHelper.deletePack(pack);
|
boolean d = dbHelper.deletePack(pack);
|
||||||
Log.d(TAG, "Delete from DB=" + d);
|
Log.d(TAG, "Delete from DB=" + d);
|
||||||
|
|
||||||
|
|
||||||
int index = packs.indexOf(pack);
|
int index = packs.indexOf(pack);
|
||||||
packs.remove(index);
|
packs.remove(index);
|
||||||
notifyPackRemoved(pack, index);
|
notifyPackRemoved(pack, index);
|
||||||
@@ -335,7 +355,7 @@ public class TelegramStickersService {
|
|||||||
uiThreadHandler.post(r);
|
uiThreadHandler.post(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncPack(TelegramStickersPack pack) {
|
private void syncPack(final TelegramStickersPack pack) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> syncPack(pack));
|
queuedTasks.add(() -> syncPack(pack));
|
||||||
return;
|
return;
|
||||||
@@ -377,4 +397,6 @@ public class TelegramStickersService {
|
|||||||
|
|
||||||
void onInactivePacksListChanged();
|
void onInactivePacksListChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,20 +7,23 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
|
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
|
||||||
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
|
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
|
||||||
import okhttp3.*;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
import ru.vtosters.lite.net.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.PasswordAuthentication;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.Socket;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,19 +31,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public class TelegramStickersGrabber {
|
public class TelegramStickersGrabber {
|
||||||
private static final String TAG = "TSG";
|
private static final String TAG = "TSG";
|
||||||
private static final OkHttpClient sClient = VtOkHttpClient.getInstance();
|
public static String REAL_TG_IP = null;
|
||||||
private static final int MAX_RETRIES = 2;
|
public static String PROXY_IP = null;
|
||||||
private static final AtomicInteger retryCount = new AtomicInteger();
|
public static int PROXY_PORT = -1;
|
||||||
private static final int STICKER_QUALITY = 100;
|
public static String PROXY_USER = null;
|
||||||
private static final String STICKER_FILE_NAME_FORMAT = "%03d.png";
|
public static String PROXY_PASS = null;
|
||||||
private static final String STICKERS_KEY = "stickers";
|
public static boolean PROXY_SOCKS = true;
|
||||||
private static final String FILE_ID_KEY = "file_id";
|
public static boolean HAS_PASS = false;
|
||||||
private static final String EMOJI_KEY = "emoji";
|
public static boolean USE_PROXY = false;
|
||||||
private static final String IS_ANIMATED_KEY = "is_animated";
|
|
||||||
private static final String IS_VIDEO_KEY = "is_video";
|
|
||||||
private static final String THUMB_KEY = "thumb";
|
|
||||||
private static final String NAME_KEY = "name";
|
|
||||||
private static final String TITLE_KEY = "title";
|
|
||||||
private static String BOT_API_BASE_URL;
|
private static String BOT_API_BASE_URL;
|
||||||
private static String GET_STICKER_SET_URL;
|
private static String GET_STICKER_SET_URL;
|
||||||
private static String GET_FILE_URL;
|
private static String GET_FILE_URL;
|
||||||
@@ -52,10 +50,12 @@ public class TelegramStickersGrabber {
|
|||||||
|
|
||||||
private final MessageDigest sha256;
|
private final MessageDigest sha256;
|
||||||
private final Handler uiThreadHandler;
|
private final Handler uiThreadHandler;
|
||||||
private String botApiKey;
|
private String botApiKey = "";
|
||||||
|
private NetClient httpClient;
|
||||||
|
|
||||||
public TelegramStickersGrabber(String botApiKey) {
|
public TelegramStickersGrabber(String botApiKey) {
|
||||||
this.botApiKey = botApiKey;
|
this.botApiKey = botApiKey;
|
||||||
|
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
|
||||||
uiThreadHandler = new Handler(Looper.getMainLooper());
|
uiThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
try {
|
try {
|
||||||
sha256 = MessageDigest.getInstance("SHA-256");
|
sha256 = MessageDigest.getInstance("SHA-256");
|
||||||
@@ -69,12 +69,81 @@ public class TelegramStickersGrabber {
|
|||||||
public static void updateURLs() {
|
public static void updateURLs() {
|
||||||
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
|
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
|
||||||
|
|
||||||
BOT_API_BASE_URL = "https://api.telegram.org/bot%s/";
|
|
||||||
|
BOT_API_BASE_URL = REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/bot%s/" : "https://api.telegram.org/bot%s/";
|
||||||
GET_STICKER_SET_URL = BOT_API_BASE_URL + "getStickerSet?name=%s";
|
GET_STICKER_SET_URL = BOT_API_BASE_URL + "getStickerSet?name=%s";
|
||||||
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
|
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void grabPack(String id, File packFolder, String installedVersion, PackDownloadListener listener) {
|
public static boolean isTelegramBlocked() {
|
||||||
|
NetClient client = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
|
||||||
|
try {
|
||||||
|
if (PROXY_IP != null) {
|
||||||
|
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
|
||||||
|
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
|
||||||
|
NetClient.Builder b = new NetClient.Builder().proxy(proxy);
|
||||||
|
if (PROXY_USER != null) {
|
||||||
|
b = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray()));
|
||||||
|
}
|
||||||
|
client = b.build();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
NetRequest req = new NetRequest.Builder().url(REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/test/" : "https://api.telegram.org/test/").build();
|
||||||
|
try {
|
||||||
|
NetResponse res = client.newCall(req).execute();
|
||||||
|
String s = res.getDataString();
|
||||||
|
|
||||||
|
if (!s.equals("{\"ok\":false,\"error_code\":404,\"description\":\"Not Found\"}"))
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetProxy() {
|
||||||
|
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableProxy() {
|
||||||
|
Thread kostil = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
if (PROXY_IP != null) {
|
||||||
|
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
|
||||||
|
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
|
||||||
|
NetClient.Builder b = httpClient.newBuilder().proxy(proxy);
|
||||||
|
|
||||||
|
if (PROXY_USER != null) {
|
||||||
|
httpClient = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray())).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient = b.build();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
kostil.start();
|
||||||
|
try {
|
||||||
|
kostil.join();
|
||||||
|
Log.d(TAG, "Proxy set");
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.e(TAG, "Unable to set proxy:");
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void grabPack(String id, File packFolder, String installedVersion, final PackDownloadListener listener) {
|
||||||
getPackInfo(id, packFolder, installedVersion, listener);
|
getPackInfo(id, packFolder, installedVersion, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,247 +151,264 @@ public class TelegramStickersGrabber {
|
|||||||
botApiKey = key;
|
botApiKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkKey(KeyCheckListener listener) {
|
public void checkKey(final KeyCheckListener listener) {
|
||||||
Log.d(TAG, "Checking key: " + botApiKey);
|
Log.d(TAG, "Checking key: " + botApiKey);
|
||||||
// Create a request object with the URL and bot API key
|
NetRequest request = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_URL + "getMe", botApiKey)).build();
|
||||||
Request request = new Request.a()
|
httpClient.newCall(request).enqueue(new NetCallback() {
|
||||||
.b(String.format(BOT_API_BASE_URL + "getMe", botApiKey))
|
|
||||||
.a();
|
|
||||||
// Create a new call object and enqueue it to run asynchronously
|
|
||||||
sClient.a(request).a(new Callback() {
|
|
||||||
@Override
|
@Override
|
||||||
public void a(Call call, IOException e) {
|
public void onFailure(NetCall call, IOException e) {
|
||||||
// Handle the failure case
|
e.printStackTrace();
|
||||||
Log.d(TAG, e.getMessage());
|
|
||||||
runOnUiThread(listener::onNetError);
|
runOnUiThread(listener::onNetError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void a(Call call, Response response) {
|
public void onResponse(NetCall call, final NetResponse response) throws IOException {
|
||||||
Log.d(TAG, response.toString());
|
runOnUiThread(() -> listener.onKeyChecked(response.code() == 200));
|
||||||
runOnUiThread(() -> listener.onKeyChecked(response.h()));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getPack(StickerSet set, File packFolder, TelegramStickersPackInfo packInfo, PackDownloadListener listener) {
|
private void getPackInfo(final String packName, final File packFolder, final String installedVersion, final PackDownloadListener listener) {
|
||||||
GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
|
NetRequest packInfoRequest = new NetRequest.Builder().get().url(String.format(GET_STICKER_SET_URL, botApiKey, packName)).build();
|
||||||
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
|
httpClient.newCall(packInfoRequest).enqueue(new NetCallback() {
|
||||||
listener.onPackDownloaded(packInfo, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
Flag deathFlag = new Flag();
|
|
||||||
|
|
||||||
for (int i = 0; i < set.stickers.size(); i++) {
|
|
||||||
Sticker sticker = set.stickers.get(i);
|
|
||||||
String getFileUrl = String.format(GET_FILE_URL, botApiKey, sticker.fileId);
|
|
||||||
Request fileInfoRequest = new Request.a()
|
|
||||||
.b(getFileUrl)
|
|
||||||
.a();
|
|
||||||
|
|
||||||
int stickerIndex = i + 1;
|
|
||||||
|
|
||||||
sClient.a(fileInfoRequest).a(new Callback() {
|
|
||||||
@Override
|
|
||||||
public void a(Call call, IOException e) {
|
|
||||||
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Call call, Response response) {
|
|
||||||
if (deathFlag.up())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!response.h()) {
|
|
||||||
retryRequest(call, fileInfoRequest, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (ResponseBody responseBody = response.a()) {
|
|
||||||
JSONObject fileInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
|
|
||||||
String filePath = fileInfoResponse.getString("file_path");
|
|
||||||
|
|
||||||
if (botApiKey == null) botApiKey = "";
|
|
||||||
|
|
||||||
String fileDownloadUrl = String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath);
|
|
||||||
Request fileDownloadRequest = new Request.a()
|
|
||||||
.b(fileDownloadUrl)
|
|
||||||
.a();
|
|
||||||
|
|
||||||
sClient.a(fileDownloadRequest).a(new Callback() {
|
|
||||||
@Override
|
|
||||||
public void a(Call call, IOException e) {
|
|
||||||
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileDownloadRequest, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Call call, Response response) {
|
|
||||||
if (deathFlag.up())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!response.h()) {
|
|
||||||
retryRequest(call, fileDownloadRequest, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (ResponseBody responseBody = response.a()) {
|
|
||||||
if (responseBody == null) {
|
|
||||||
handleFailure(deathFlag, listener, new TSGException("Response body for a sticker is null :/"), downloadedStickers, call, fileDownloadRequest, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream stickerInputStream = responseBody.a();
|
|
||||||
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
|
|
||||||
|
|
||||||
if (stickerImage == null) {
|
|
||||||
handleFailure(deathFlag, listener, new TSGException("Unable to decode sticker image"), downloadedStickers, call, fileDownloadRequest, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File stickerFile = new File(packFolder, String.format(STICKER_FILE_NAME_FORMAT, stickerIndex));
|
|
||||||
|
|
||||||
try {
|
|
||||||
saveStickerImage(stickerImage, stickerFile);
|
|
||||||
} catch (TSGException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex,
|
|
||||||
downloadedStickers.value() + 1, set.stickers.size());
|
|
||||||
downloadedStickers.increase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleFailure(Flag deathFlag, PackDownloadListener listener, Exception e, GoalCounter downloadedStickers, Call call, Request request, Callback callback) {
|
|
||||||
if ((e instanceof IOException || e instanceof TSGException) && retryCount.get() > MAX_RETRIES) {
|
|
||||||
Log.e(TAG, "Unable to download sticker: " + e.getMessage());
|
|
||||||
downloadedStickers.increase();
|
|
||||||
return;
|
|
||||||
} else if (retryCount.get() < MAX_RETRIES) {
|
|
||||||
retryRequest(call, request, callback);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deathFlag.up()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
deathFlag.raise();
|
|
||||||
listener.onPackDownloadError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void retryRequest(Call call, Request request, Callback callback) {
|
|
||||||
retryCount.incrementAndGet();
|
|
||||||
|
|
||||||
if (retryCount.get() > MAX_RETRIES) {
|
|
||||||
Log.e(TAG, "retryRequest: max retries");
|
|
||||||
callback.a(call, new IOException());
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "retryRequest");
|
|
||||||
sClient.a(request).a(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveStickerImage(Bitmap image, File file) throws TSGException {
|
|
||||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
||||||
image.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new TSGException("Unable to save sticker image");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getPackInfo(String packName, File packFolder, String installedVersion, PackDownloadListener listener) {
|
|
||||||
String getStickerSetUrl = String.format(GET_STICKER_SET_URL, botApiKey, packName);
|
|
||||||
Request packInfoRequest = new Request.a()
|
|
||||||
.b(getStickerSetUrl)
|
|
||||||
.a();
|
|
||||||
sClient.a(packInfoRequest).a(new Callback() {
|
|
||||||
@Override
|
@Override
|
||||||
public void a(Call call, IOException e) {
|
public void onFailure(NetCall call, IOException e) {
|
||||||
Log.d(TAG, e.getMessage());
|
|
||||||
listener.onPackDownloadError(e);
|
listener.onPackDownloadError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void a(Call call, Response response) throws IOException {
|
public void onResponse(NetCall call, NetResponse response) {
|
||||||
Log.d(TAG, response.toString());
|
if (!response.isSuccessful()) {
|
||||||
|
if (response.getData() != null)
|
||||||
|
listener.onPackDownloadError(new TSGException(response.getDataString() + "\nURL: " + call.request().url()));
|
||||||
|
else
|
||||||
|
listener.onPackDownloadError(new TSGException("Unknown Exception, no response body"));
|
||||||
|
|
||||||
if (!response.h()) {
|
|
||||||
String errorMessage = response.a() != null ? response.a().g() + "\nURL: " + call.m0().g() : "Unknown Exception, no response body";
|
|
||||||
listener.onPackDownloadError(new TSGException(errorMessage));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ResponseBody responseBody = response.a()) {
|
StickerSet set;
|
||||||
JSONObject packInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
|
try {
|
||||||
String packVersion = new String(sha256.digest(packInfoResponse.toString().getBytes(StandardCharsets.UTF_8)));
|
JSONObject jPackInfo = new JSONObject(response.getDataString()).getJSONObject("result");
|
||||||
|
String packVersion = new String(sha256.digest(jPackInfo.toString().getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
if (packVersion.equals(installedVersion)) {
|
if (packVersion.equals(installedVersion)) {
|
||||||
listener.onPackDownloaded(null, false);
|
listener.onPackDownloaded(null, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONArray stickersArray = packInfoResponse.getJSONArray(STICKERS_KEY);
|
JSONArray jStickers = jPackInfo.getJSONArray("stickers");
|
||||||
|
|
||||||
if (stickersArray.length() == 0) {
|
if (jStickers.length() == 0) {
|
||||||
listener.onPackDownloadError(new TSGException("No stickers in pack"));
|
listener.onPackDownloadError(new TSGException("No stickers in pack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Sticker> stickers = parseStickers(stickersArray);
|
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
|
||||||
|
|
||||||
StickerSet set = new StickerSet(packInfoResponse.getString(NAME_KEY), packInfoResponse.getString(TITLE_KEY), packVersion, stickers);
|
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
|
||||||
|
|
||||||
if (!packFolder.exists() && !packFolder.mkdirs()) {
|
for (int i = 0; i < jStickers.length(); i++) {
|
||||||
// Handle the folder creation failure case
|
JSONObject jSticker = jStickers.getJSONObject(i);
|
||||||
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
|
if (jSticker.optBoolean("is_animated") || jSticker.optBoolean("is_video")) {
|
||||||
return;
|
if (jSticker.has("thumb")) {
|
||||||
|
stickers.add(new Sticker(jSticker.getJSONObject("thumb").getString("file_id"), jSticker.getString("emoji")));
|
||||||
|
} else {
|
||||||
|
listener.onPackDownloadError(new TSGException("Animated and video stickerpacks without thumbs are not supported!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stickers.add(new Sticker(jSticker.getString("file_id"), jSticker.getString("emoji")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, String.format("Got info for pack %s, now downloading stickers to %s", packName, packFolder.getAbsolutePath()));
|
set = new StickerSet(jPackInfo.getString("name"), jPackInfo.getString("title"), packVersion, stickers);
|
||||||
|
|
||||||
TelegramStickersPackInfo packInfo = new TelegramStickersPackInfo(packName, set.name, set.stickers.size(), set.version);
|
|
||||||
|
|
||||||
listener.onGotPackInfo(packInfo);
|
|
||||||
|
|
||||||
getPack(set, packFolder, packInfo, listener);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
listener.onPackDownloadError(e);
|
listener.onPackDownloadError(e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!packFolder.exists() && !packFolder.mkdirs()) {
|
||||||
|
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, String.format("Got info for pack %s, now downloading stickers to %s", packName, packFolder.getAbsolutePath()));
|
||||||
|
|
||||||
|
TelegramStickersPackInfo packInfo = new TelegramStickersPackInfo(packName, set.name, set.stickers.size(), set.version);
|
||||||
|
listener.onGotPackInfo(packInfo);
|
||||||
|
getPack(set, packFolder, packInfo, listener);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<Sticker> parseStickers(JSONArray stickersArray) throws JSONException, TSGException {
|
private void getPack(final StickerSet set, final File packFolder, final TelegramStickersPackInfo packInfo, final PackDownloadListener listener) {
|
||||||
ArrayList<Sticker> stickers = new ArrayList<>(stickersArray.length());
|
final GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
|
||||||
|
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
|
||||||
|
|
||||||
for (int i = 0; i < stickersArray.length(); i++) {
|
listener.onPackDownloaded(packInfo, true);
|
||||||
JSONObject stickerObject = stickersArray.getJSONObject(i);
|
});
|
||||||
boolean isAnimated = stickerObject.optBoolean(IS_ANIMATED_KEY);
|
final Flag deathFlag = new Flag();
|
||||||
boolean isVideo = stickerObject.optBoolean(IS_VIDEO_KEY);
|
|
||||||
String fileId = stickerObject.getString(FILE_ID_KEY);
|
|
||||||
String emoji = stickerObject.getString(EMOJI_KEY);
|
|
||||||
|
|
||||||
if (isAnimated || isVideo) {
|
AtomicInteger retryed = new AtomicInteger();
|
||||||
if (stickerObject.has(THUMB_KEY)) {
|
for (int i = 0; i < set.stickers.size(); i++) {
|
||||||
fileId = stickerObject.getJSONObject(THUMB_KEY).getString(FILE_ID_KEY);
|
final Sticker sticker = set.stickers.get(i);
|
||||||
} else {
|
NetRequest fileInfoRequest = new NetRequest.Builder().get().url(String.format(GET_FILE_URL, botApiKey, sticker.fileId)).build();
|
||||||
throw new TSGException("Animated and video stickerpacks without thumbs are not supported!");
|
|
||||||
|
final int stickerIndex = i + 1;
|
||||||
|
|
||||||
|
AtomicBoolean sync = new AtomicBoolean();
|
||||||
|
httpClient.newCall(fileInfoRequest).enqueue(new NetCallback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(NetCall call, IOException e) {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
|
||||||
|
sync.set(true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
stickers.add(new Sticker(fileId, emoji));
|
@Override
|
||||||
|
public void onResponse(NetCall call, NetResponse response) throws IOException {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (response.code() != 200) {
|
||||||
|
retryed.incrementAndGet();
|
||||||
|
|
||||||
|
if (retryed.get() > 5)
|
||||||
|
onFailure(call, new IOException());
|
||||||
|
else httpClient.newCall(fileInfoRequest)
|
||||||
|
.enqueue(this);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filePath;
|
||||||
|
try {
|
||||||
|
JSONObject jFileInfo = new JSONObject(response.getDataString()).getJSONObject("result");
|
||||||
|
filePath = jFileInfo.getString("file_path");
|
||||||
|
} catch (Exception e) {
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (botApiKey == null) botApiKey = "";
|
||||||
|
|
||||||
|
NetRequest fileDownloadRequest = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath)).build();
|
||||||
|
|
||||||
|
httpClient.newCall(fileDownloadRequest).enqueue(new NetCallback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(NetCall call, IOException e) {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
|
||||||
|
sync.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(NetCall call, NetResponse response) throws IOException {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (response.code() != 200) {
|
||||||
|
retryed.incrementAndGet();
|
||||||
|
|
||||||
|
if (retryed.get() > 5)
|
||||||
|
onFailure(call, new IOException());
|
||||||
|
else httpClient.newCall(fileDownloadRequest)
|
||||||
|
.enqueue(this);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getData() == null) {
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(new TSGException("Response body for a sticker is null :/"));
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream stickerInputStream = response.getDataStream();
|
||||||
|
|
||||||
|
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
|
||||||
|
|
||||||
|
try {
|
||||||
|
stickerInputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stickerImage == null) {
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(new TSGException("Unable to decode sticker image"));
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File stickerFile = new File(packFolder, String.format("%03d.png", stickerIndex));
|
||||||
|
|
||||||
|
FileOutputStream fileOutputStream;
|
||||||
|
try {
|
||||||
|
fileOutputStream = new FileOutputStream(stickerFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stickerImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
|
||||||
|
stickerImage.recycle();
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(new TSGException("Unable to compress sticker to a png file at path: " + stickerFile.getAbsolutePath()));
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stickerImage.recycle();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fileOutputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
sync.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex, downloadedStickers.value() + 1, set.stickers.size());
|
||||||
|
downloadedStickers.increase();
|
||||||
|
|
||||||
|
sync.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
while (!sync.get())
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stickers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runOnUiThread(Runnable r) {
|
private void runOnUiThread(Runnable r) {
|
||||||
@@ -345,13 +431,15 @@ public class TelegramStickersGrabber {
|
|||||||
void onNetError();
|
void onNetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TSGException extends Exception {
|
public class TSGException extends Exception {
|
||||||
|
private static final long serialVersionUID = 7866915509988422944L;
|
||||||
|
|
||||||
private TSGException(String message) {
|
private TSGException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StickerSet {
|
private class StickerSet {
|
||||||
String id;
|
String id;
|
||||||
String name;
|
String name;
|
||||||
String version;
|
String version;
|
||||||
@@ -365,7 +453,7 @@ public class TelegramStickersGrabber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Sticker {
|
private class Sticker {
|
||||||
String emoji;
|
String emoji;
|
||||||
String fileId;
|
String fileId;
|
||||||
|
|
||||||
|
|||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
package com.arthenica.ffmpegkit;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.arthenica.smartexception.java.Exceptions;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NativeLoader {
|
||||||
|
static final String[] FFMPEG_LIBRARIES = {"avutil", "swresample", "avcodec", "avformat", "avfilter", "avdevice"};
|
||||||
|
|
||||||
|
private static void loadLibrary(String libraryName) {
|
||||||
|
System.loadLibrary(libraryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> loadExternalLibraries() {
|
||||||
|
//return Packages.getExternalLibraries();
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String loadNativeAbi() {
|
||||||
|
return AbiDetect.getNativeAbi();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String loadAbi() {
|
||||||
|
return AbiDetect.getAbi();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String loadPackageName() {
|
||||||
|
return Packages.getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String loadVersion() {
|
||||||
|
return FFmpegKitConfig.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean loadIsLTSBuild() {
|
||||||
|
return AbiDetect.isNativeLTSBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int loadLogLevel() {
|
||||||
|
return FFmpegKitConfig.getNativeLogLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static String loadBuildDate() {
|
||||||
|
return FFmpegKitConfig.getBuildDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enableRedirection() {
|
||||||
|
FFmpegKitConfig.enableRedirection();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadFFmpegKitAbiDetect() {
|
||||||
|
loadLibrary("ffmpegkit_abidetect");
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean loadFFmpeg() {
|
||||||
|
boolean nativeFFmpegLoaded = false;
|
||||||
|
boolean nativeFFmpegTriedAndFailed = false;
|
||||||
|
|
||||||
|
if (AbiDetect.ARM_V7A.equals(loadNativeAbi())) {
|
||||||
|
try {
|
||||||
|
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
|
||||||
|
loadLibrary(ffmpegLibrary + "_neon");
|
||||||
|
}
|
||||||
|
nativeFFmpegLoaded = true;
|
||||||
|
} catch (final Error e) {
|
||||||
|
android.util.Log.i(FFmpegKitConfig.TAG, String.format("NEON supported armeabi-v7a ffmpeg library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
|
||||||
|
nativeFFmpegTriedAndFailed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeFFmpegLoaded) {
|
||||||
|
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
|
||||||
|
loadLibrary(ffmpegLibrary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nativeFFmpegTriedAndFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void loadFFmpegKit(boolean nativeFFmpegTriedAndFailed) {
|
||||||
|
boolean nativeFFmpegKitLoaded = false;
|
||||||
|
if (!nativeFFmpegTriedAndFailed && "arm-v7a".equals(loadNativeAbi())) {
|
||||||
|
try {
|
||||||
|
loadLibrary("ffmpegkit_armv7a_neon");
|
||||||
|
nativeFFmpegKitLoaded = true;
|
||||||
|
AbiDetect.setArmV7aNeonLoaded();
|
||||||
|
} catch (Error e) {
|
||||||
|
Log.i("ffmpeg-kit", String.format("NEON supported armeabi-v7a ffmpegkit library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nativeFFmpegKitLoaded) {
|
||||||
|
loadLibrary("ffmpegkit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static String getDeviceDebugInformation() {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append("brand: ");
|
||||||
|
stringBuilder.append(Build.BRAND);
|
||||||
|
stringBuilder.append(", model: ");
|
||||||
|
stringBuilder.append(Build.MODEL);
|
||||||
|
stringBuilder.append(", device: ");
|
||||||
|
stringBuilder.append(Build.DEVICE);
|
||||||
|
stringBuilder.append(", api level: ");
|
||||||
|
stringBuilder.append(Build.VERSION.SDK_INT);
|
||||||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
stringBuilder.append(", abis: ");
|
||||||
|
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_ABIS));
|
||||||
|
stringBuilder.append(", 32bit abis: ");
|
||||||
|
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_32_BIT_ABIS));
|
||||||
|
stringBuilder.append(", 64bit abis: ");
|
||||||
|
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_64_BIT_ABIS));
|
||||||
|
} else {
|
||||||
|
stringBuilder.append(", cpu abis: ");
|
||||||
|
stringBuilder.append(Build.CPU_ABI);
|
||||||
|
stringBuilder.append(", cpu abi2s: ");
|
||||||
|
stringBuilder.append(Build.CPU_ABI2);
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,732 @@
|
|||||||
|
/*
|
||||||
|
* BSD 3-Clause License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, Taner Sener
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.arthenica.smartexception;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Abstract class that includes common variables and methods for different <code>Exceptions</code> implementations.
|
||||||
|
*
|
||||||
|
* @author Taner Sener
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractExceptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Default max depth used in methods which do not have a maxDepth argument.
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_MAX_DEPTH = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Default value for ignoring all causes when stack trace elements are printed or converted to string.
|
||||||
|
*/
|
||||||
|
public static final boolean DEFAULT_IGNORE_ALL_CAUSES = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores global root package names.
|
||||||
|
*/
|
||||||
|
public static final Set<String> rootPackageSet = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores global group package names.
|
||||||
|
*/
|
||||||
|
public static final Set<String> groupPackageSet = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores global ignore package names.
|
||||||
|
*/
|
||||||
|
public static final Set<String> ignorePackageSet = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores global ignore cause package names.
|
||||||
|
*/
|
||||||
|
public static final Set<String> ignoreCausePackageSet = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores the value of global ignore all causes option.
|
||||||
|
*/
|
||||||
|
public static boolean ignoreAllCauses = DEFAULT_IGNORE_ALL_CAUSES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Stores the global stack trace serializer implementation.
|
||||||
|
*/
|
||||||
|
public static StackTraceElementSerializer stackTraceElementSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new root package.
|
||||||
|
*
|
||||||
|
* @param packageString root package name to register
|
||||||
|
*/
|
||||||
|
public static void registerRootPackage(final String packageString) {
|
||||||
|
rootPackageSet.add(packageString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered root packages.
|
||||||
|
*/
|
||||||
|
public static void clearRootPackages() {
|
||||||
|
rootPackageSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new group package.
|
||||||
|
*
|
||||||
|
* @param packageString group package name to register
|
||||||
|
*/
|
||||||
|
public static void registerGroupPackage(final String packageString) {
|
||||||
|
groupPackageSet.add(packageString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered group packages.
|
||||||
|
*/
|
||||||
|
public static void clearGroupPackages() {
|
||||||
|
groupPackageSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
|
||||||
|
* objects in <code>getStackTraceString</code> methods.
|
||||||
|
*
|
||||||
|
* @return current stack trace serializer implementation
|
||||||
|
*/
|
||||||
|
public static StackTraceElementSerializer getStackTraceElementSerializer() {
|
||||||
|
return stackTraceElementSerializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
|
||||||
|
* objects in <code>getStackTraceString</code> methods.
|
||||||
|
*
|
||||||
|
* @param stackTraceElementSerializer new stack trace serializer implementation
|
||||||
|
*/
|
||||||
|
public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) {
|
||||||
|
AbstractExceptions.stackTraceElementSerializer = stackTraceElementSerializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new ignore package.
|
||||||
|
*
|
||||||
|
* @param packageString ignore package name to register
|
||||||
|
* @param ignoreCauseClasses ignore cause classes from this package too
|
||||||
|
*/
|
||||||
|
public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) {
|
||||||
|
ignorePackageSet.add(packageString);
|
||||||
|
if (ignoreCauseClasses) {
|
||||||
|
ignoreCausePackageSet.add(packageString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered ignore packages.
|
||||||
|
*/
|
||||||
|
public static void clearIgnorePackages() {
|
||||||
|
ignorePackageSet.clear();
|
||||||
|
ignoreCausePackageSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the value of ignore all causes option.
|
||||||
|
*
|
||||||
|
* @return the value of global ignore all causes option. If value is true then stack trace elements printed or
|
||||||
|
* converted to string will not include causes. If value is false causes will be appended to the stack trace of
|
||||||
|
* the main throwable.
|
||||||
|
*/
|
||||||
|
public static boolean getIgnoreAllCauses() {
|
||||||
|
return ignoreAllCauses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the value of ignore all causes option.
|
||||||
|
*
|
||||||
|
* @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed
|
||||||
|
* or converted to string will not include causes. If value is false causes will be appended to the stack trace of
|
||||||
|
* the main throwable.
|
||||||
|
*/
|
||||||
|
public static void setIgnoreAllCauses(final boolean ignoreAllCauses) {
|
||||||
|
AbstractExceptions.ignoreAllCauses = ignoreAllCauses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code>.
|
||||||
|
*
|
||||||
|
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
|
||||||
|
* by {@link #registerGroupPackage(String)} and ignore packages registered by
|
||||||
|
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable) {
|
||||||
|
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code>.
|
||||||
|
*
|
||||||
|
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
|
||||||
|
* by {@link #registerGroupPackage(String)} and ignore packages registered by
|
||||||
|
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) {
|
||||||
|
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param groupPackageSet group packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet) {
|
||||||
|
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param groupPackageSet group packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final boolean ignoreAllCauses) {
|
||||||
|
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackage root package to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final String rootPackage) {
|
||||||
|
return getStackTraceString(throwable, false, Collections.singleton(rootPackage), new HashSet<String>(), new HashSet<String>(), 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackage root package to use for building the stack trace
|
||||||
|
* @param groupPackage group package to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) {
|
||||||
|
return getStackTraceString(throwable, false, Collections.singleton(rootPackage), Collections.singleton(groupPackage), new HashSet<String>(), 0, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be used
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final int maxDepth) {
|
||||||
|
return getStackTraceString(throwable, false, new HashSet<String>(), new HashSet<String>(), new HashSet<String>(), maxDepth, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be used
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) {
|
||||||
|
return getStackTraceString(throwable, false, new HashSet<String>(), new HashSet<String>(), new HashSet<String>(), maxDepth, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using parameters provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param isCause throwable is a cause or not
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param groupPackageSet group packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @param maxDepth max depth in exception chain that will be used
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final boolean isCause, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final int maxDepth, final boolean ignoreAllCauses) {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
if (throwable == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
final String className = throwable.getClass().getName();
|
||||||
|
|
||||||
|
final StackTraceElement[] stackTraceElements;
|
||||||
|
if (maxDepth > 0) {
|
||||||
|
stackTraceElements = getStackTrace(throwable, maxDepth);
|
||||||
|
} else {
|
||||||
|
stackTraceElements = getStackTrace(throwable, rootPackageSet, ignorePackageSet);
|
||||||
|
}
|
||||||
|
String message = throwable.getLocalizedMessage();
|
||||||
|
if (isEmpty(message)) {
|
||||||
|
message = throwable.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PRINT MESSAGE */
|
||||||
|
if (isCause) {
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
builder.append("Caused by: ");
|
||||||
|
builder.append(className);
|
||||||
|
if (!isEmpty(message)) {
|
||||||
|
builder.append(": ");
|
||||||
|
builder.append(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
builder.append(className);
|
||||||
|
if (!isEmpty(message)) {
|
||||||
|
builder.append(": ");
|
||||||
|
builder.append(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PRINT SHORT STACK TRACE */
|
||||||
|
String currentGroupPackage = null;
|
||||||
|
StackTraceElement firstStackTraceElementInTheGroup = null;
|
||||||
|
int currentGroupCount = 0;
|
||||||
|
for (StackTraceElement traceElement : stackTraceElements) {
|
||||||
|
String traceElementClassName = traceElement.getClassName();
|
||||||
|
String groupPackageMatch = getContainingPackage(traceElementClassName, groupPackageSet);
|
||||||
|
|
||||||
|
if (groupPackageMatch != null) {
|
||||||
|
if (!groupPackageMatch.equals(currentGroupPackage)) {
|
||||||
|
appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
|
||||||
|
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
builder.append("\tat ");
|
||||||
|
|
||||||
|
currentGroupPackage = groupPackageMatch;
|
||||||
|
firstStackTraceElementInTheGroup = traceElement;
|
||||||
|
currentGroupCount = 1;
|
||||||
|
} else {
|
||||||
|
currentGroupCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentGroupCount = appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
|
||||||
|
|
||||||
|
builder.append(System.lineSeparator());
|
||||||
|
builder.append("\tat ");
|
||||||
|
if (stackTraceElementSerializer == null) {
|
||||||
|
throw new IllegalArgumentException("Stack trace element serializer not initialized.");
|
||||||
|
} else {
|
||||||
|
builder.append(stackTraceElementSerializer.toString(traceElement));
|
||||||
|
}
|
||||||
|
currentGroupPackage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
|
||||||
|
|
||||||
|
final Throwable cause = throwable.getCause();
|
||||||
|
if (cause != null && !containsPackage(className, ignoreCausePackageSet) && !ignoreAllCauses) {
|
||||||
|
builder.append(getStackTraceString(cause, true, rootPackageSet, groupPackageSet, ignorePackageSet, maxDepth, ignoreAllCauses));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Appends stack trace group information for the given parameters.
|
||||||
|
*
|
||||||
|
* @param stringBuilder string builder that group stack trace information will be appended
|
||||||
|
* @param currentGroupPackage package name of the current group
|
||||||
|
* @param numberOfElementsInTheCurrentGroup number of elements in the current group
|
||||||
|
* @param firstStackTraceElementInTheGroup first stack trace element of this group
|
||||||
|
* @return new value for the group element count
|
||||||
|
*/
|
||||||
|
public static int appendStackTraceGroupElement(final StringBuilder stringBuilder, final String currentGroupPackage, final int numberOfElementsInTheCurrentGroup, final StackTraceElement firstStackTraceElementInTheGroup) {
|
||||||
|
if (numberOfElementsInTheCurrentGroup > 0) {
|
||||||
|
if (stackTraceElementSerializer == null) {
|
||||||
|
throw new IllegalArgumentException("Stack trace element serializer not initialized.");
|
||||||
|
} else {
|
||||||
|
stringBuilder.append((numberOfElementsInTheCurrentGroup == 1) ? stackTraceElementSerializer.toString(firstStackTraceElementInTheGroup) : String.format("%s%s ... %d more", stackTraceElementSerializer.getModuleName(firstStackTraceElementInTheGroup), currentGroupPackage, (numberOfElementsInTheCurrentGroup - 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns true if package of the given class name matches one of package names provided in
|
||||||
|
* <code>packageSet</code>.
|
||||||
|
*
|
||||||
|
* @param fullClassName fully qualified class name
|
||||||
|
* @param packageSet set containing package names
|
||||||
|
* @return true if package of the given class name matches one of package names provided in <code>packageSet</code>,
|
||||||
|
* false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsPackage(final String fullClassName, final Set<String> packageSet) {
|
||||||
|
return getContainingPackage(fullClassName, packageSet) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if one of the package names in the <code>packageSet</code> matches the package of the given class name
|
||||||
|
* and returns the matching package name.
|
||||||
|
*
|
||||||
|
* @param fullClassName fully qualified class name
|
||||||
|
* @param packageSet set containing package names
|
||||||
|
* @return one of the package names in the <code>packageSet</code> that matches the package of the given class name
|
||||||
|
* or null if no matching package name is found in the <code>packageSet</code>
|
||||||
|
*/
|
||||||
|
public static String getContainingPackage(final String fullClassName, final Set<String> packageSet) {
|
||||||
|
for (final String parentExceptionPackage : packageSet) {
|
||||||
|
if (fullClassName.startsWith(parentExceptionPackage)) {
|
||||||
|
return parentExceptionPackage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns all messages found in the exception chain of the <code>throwable</code> as a single string.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return a string containing all messages found in the exception chain
|
||||||
|
*/
|
||||||
|
public static String getAllMessages(final Throwable throwable) {
|
||||||
|
final StringBuilder messageBuilder = new StringBuilder();
|
||||||
|
getAllMessages(throwable, messageBuilder);
|
||||||
|
return messageBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Adds all messages found in the exception chain of the <code>throwable</code> into the given message builder.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param messageBuilder message builder
|
||||||
|
*/
|
||||||
|
public static void getAllMessages(final Throwable throwable, final StringBuilder messageBuilder) {
|
||||||
|
if (throwable != null) {
|
||||||
|
final String message = throwable.getMessage();
|
||||||
|
if (!isEmpty(message)) {
|
||||||
|
if (messageBuilder.length() != 0) {
|
||||||
|
messageBuilder.append(System.lineSeparator());
|
||||||
|
messageBuilder.append(" - Caused by: ");
|
||||||
|
}
|
||||||
|
messageBuilder.append(message);
|
||||||
|
}
|
||||||
|
getAllMessages(throwable.getCause(), messageBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Builds a smart stack trace for the given <code>throwable</code> using elements found until the maxDepth and
|
||||||
|
* elements of it.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be returned
|
||||||
|
* @return an array containing stack trace elements
|
||||||
|
*/
|
||||||
|
public static StackTraceElement[] getStackTrace(final Throwable throwable, final int maxDepth) {
|
||||||
|
final ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
|
||||||
|
|
||||||
|
if (throwable != null) {
|
||||||
|
StackTraceElement[] stackTrace = throwable.getStackTrace();
|
||||||
|
for (int i = 0; (i < stackTrace.length) && (i < maxDepth); i++) {
|
||||||
|
list.add(stackTrace[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.toArray(new StackTraceElement[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Builds a smart stack trace for the given <code>throwable</code> using packages provided and returns elements
|
||||||
|
* of it.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @return an array containing stack trace elements
|
||||||
|
*/
|
||||||
|
public static StackTraceElement[] getStackTrace(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> ignorePackageSet) {
|
||||||
|
final ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
|
||||||
|
final ArrayList<StackTraceElement> partialList = new ArrayList<StackTraceElement>();
|
||||||
|
|
||||||
|
if (throwable != null) {
|
||||||
|
for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {
|
||||||
|
final String className = stackTraceElement.getClassName();
|
||||||
|
if (!isEmpty(className)) {
|
||||||
|
if (containsPackage(className, rootPackageSet)) {
|
||||||
|
list.addAll(partialList);
|
||||||
|
list.add(stackTraceElement);
|
||||||
|
} else if (!containsPackage(className, ignorePackageSet)) {
|
||||||
|
partialList.add(stackTraceElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
list.addAll(partialList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.toArray(new StackTraceElement[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns true if the given cause class is found in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link #DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @return true if the given cause class is found in the exception chain of the <code>throwable</code>, false
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass) {
|
||||||
|
return containsCause(throwable, causeClass, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns true if the given cause class and cause message is found in the exception chain of the
|
||||||
|
* <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link #DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @return true if the given cause class and cause message is found in the exception chain of the
|
||||||
|
* <code>throwable</code>, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
|
||||||
|
return (searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return the cause of the <code>throwable</code> found or null if <code>throwable</code> does not have a cause
|
||||||
|
*/
|
||||||
|
public static Throwable getCause(final Throwable throwable) {
|
||||||
|
return getCause(throwable, DEFAULT_MAX_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Note that this method walks through the exception chain up to given <code>maxDepth</code>. If exception chain
|
||||||
|
* includes more items than <code>maxDepth</code>, cause found at <code>maxDepth</code> level is returned.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the cause of the <code>throwable</code> found until <code>maxDepth</code> is reached in the exception
|
||||||
|
* chain or null if <code>throwable</code> does not have a cause
|
||||||
|
*/
|
||||||
|
public static Throwable getCause(final Throwable throwable, final int maxDepth) {
|
||||||
|
if (throwable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxDepth <= 0) {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Throwable cause = throwable.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
return throwable;
|
||||||
|
} else {
|
||||||
|
return getCause(cause, maxDepth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link #DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass) {
|
||||||
|
return searchCause(throwable, causeClass, null, DEFAULT_MAX_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link #DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
* and cause message
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
|
||||||
|
return searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
|
||||||
|
* <code>maxDepth</code>.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
* and cause message
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage, final int maxDepth) {
|
||||||
|
if (throwable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(causeMessage)) {
|
||||||
|
if (throwable.getClass().equals(causeClass)) {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (throwable.getClass().equals(causeClass) && getAllMessages(throwable).toLowerCase().contains(causeMessage.toLowerCase())) {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxDepth <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Throwable cause = throwable.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return searchCause(cause, causeClass, causeMessage, maxDepth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
|
||||||
|
* <code>maxDepth</code>.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final int maxDepth) {
|
||||||
|
if (throwable == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (throwable.getClass().equals(causeClass)) {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxDepth <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Throwable cause = throwable.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return searchCause(cause, causeClass, maxDepth - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks if given string is empty or not.
|
||||||
|
*
|
||||||
|
* @param value string to check
|
||||||
|
* @return true if given string is null or has zero length after trim(), false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(final String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.trim().length() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the package name of this class.
|
||||||
|
*
|
||||||
|
* @param className fully qualified class name
|
||||||
|
* @return name of the package
|
||||||
|
*/
|
||||||
|
public static String packageName(final String className) {
|
||||||
|
if (className == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = className.lastIndexOf(".");
|
||||||
|
if (index >= 0) {
|
||||||
|
return className.substring(0, index);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* BSD 3-Clause License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, Taner Sener
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.arthenica.smartexception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Base interface for classes which can serialize {@link StackTraceElement} objects.
|
||||||
|
*
|
||||||
|
* @author Taner Sener
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
|
public interface StackTraceElementSerializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Converts the given <code>stackTraceElement</code> into a string.
|
||||||
|
*
|
||||||
|
* @param stackTraceElement stack trace element to convert
|
||||||
|
* @return string representing the given <code>stackTraceElement</code>
|
||||||
|
*/
|
||||||
|
String toString(final StackTraceElement stackTraceElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns module name for the given <code>stackTraceElement</code>.
|
||||||
|
*
|
||||||
|
* @param stackTraceElement stack trace element to use
|
||||||
|
* @return module name of the given <code>stackTraceElement</code> or an empty string if implementation does not
|
||||||
|
* support printing module names
|
||||||
|
*/
|
||||||
|
String getModuleName(final StackTraceElement stackTraceElement);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns string definition used to print native methods.
|
||||||
|
*
|
||||||
|
* @return string definition used to print native methods
|
||||||
|
*/
|
||||||
|
String getNativeMethodDefinition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns string definition used to print methods with unknown source.
|
||||||
|
*
|
||||||
|
* @return string definition used to print methods with unknown source
|
||||||
|
*/
|
||||||
|
String getUnknownSourceDefinition();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
/*
|
||||||
|
* BSD 3-Clause License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, Taner Sener
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.arthenica.smartexception.java;
|
||||||
|
|
||||||
|
import com.arthenica.smartexception.AbstractExceptions;
|
||||||
|
import com.arthenica.smartexception.StackTraceElementSerializer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Utility class to handle Throwable objects and stack traces. This implementation is compatible with all Java and
|
||||||
|
* Android versions.
|
||||||
|
*
|
||||||
|
* @author Taner Sener
|
||||||
|
* @since 0.1.0
|
||||||
|
*/
|
||||||
|
public class Exceptions {
|
||||||
|
|
||||||
|
static {
|
||||||
|
AbstractExceptions.setStackTraceElementSerializer(new StackTraceElementSerializer() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(StackTraceElement stackTraceElement) {
|
||||||
|
final StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
stringBuilder.append(stackTraceElement.getClassName());
|
||||||
|
stringBuilder.append(".");
|
||||||
|
stringBuilder.append(stackTraceElement.getMethodName());
|
||||||
|
|
||||||
|
if (stackTraceElement.isNativeMethod()) {
|
||||||
|
stringBuilder.append("(Native Method)");
|
||||||
|
} else if ((stackTraceElement.getFileName() != null) && (stackTraceElement.getFileName().length() > 0)) {
|
||||||
|
stringBuilder.append("(");
|
||||||
|
stringBuilder.append(stackTraceElement.getFileName());
|
||||||
|
if (stackTraceElement.getLineNumber() >= 0) {
|
||||||
|
stringBuilder.append(":");
|
||||||
|
stringBuilder.append(stackTraceElement.getLineNumber());
|
||||||
|
}
|
||||||
|
stringBuilder.append(")");
|
||||||
|
} else {
|
||||||
|
stringBuilder.append("(Unknown Source)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModuleName(StackTraceElement stackTraceElement) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNativeMethodDefinition() {
|
||||||
|
return "(Native Method)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUnknownSourceDefinition() {
|
||||||
|
return "(Unknown Source)";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new root package.
|
||||||
|
*
|
||||||
|
* @param packageString root package name to register
|
||||||
|
*/
|
||||||
|
public static void registerRootPackage(final String packageString) {
|
||||||
|
AbstractExceptions.registerRootPackage(packageString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered root packages.
|
||||||
|
*/
|
||||||
|
public static void clearRootPackages() {
|
||||||
|
AbstractExceptions.clearRootPackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new group package.
|
||||||
|
*
|
||||||
|
* @param packageString group package name to register
|
||||||
|
*/
|
||||||
|
public static void registerGroupPackage(final String packageString) {
|
||||||
|
AbstractExceptions.registerGroupPackage(packageString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered group packages.
|
||||||
|
*/
|
||||||
|
public static void clearGroupPackages() {
|
||||||
|
AbstractExceptions.clearGroupPackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
|
||||||
|
* objects in <code>getStackTraceString</code> methods.
|
||||||
|
*
|
||||||
|
* @return current stack trace serializer implementation
|
||||||
|
*/
|
||||||
|
public static StackTraceElementSerializer getStackTraceElementSerializer() {
|
||||||
|
return AbstractExceptions.getStackTraceElementSerializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
|
||||||
|
* objects in <code>getStackTraceString</code> methods.
|
||||||
|
*
|
||||||
|
* @param stackTraceElementSerializer new stack trace serializer implementation
|
||||||
|
*/
|
||||||
|
public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) {
|
||||||
|
AbstractExceptions.setStackTraceElementSerializer(stackTraceElementSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Registers a new ignore package.
|
||||||
|
*
|
||||||
|
* @param packageString ignore package name to register
|
||||||
|
* @param ignoreCauseClasses ignore cause classes from this package too
|
||||||
|
*/
|
||||||
|
public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) {
|
||||||
|
AbstractExceptions.registerIgnorePackage(packageString, ignoreCauseClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Clears previously registered ignore packages.
|
||||||
|
*/
|
||||||
|
public static void clearIgnorePackages() {
|
||||||
|
AbstractExceptions.clearIgnorePackages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the value of ignore all causes option.
|
||||||
|
*
|
||||||
|
* @return the value of global ignore all causes option. If value is true then stack trace elements printed or
|
||||||
|
* converted to string will not include causes. If value is false causes will be appended to the stack trace of
|
||||||
|
* the main throwable.
|
||||||
|
*/
|
||||||
|
public static boolean getIgnoreAllCauses() {
|
||||||
|
return AbstractExceptions.getIgnoreAllCauses();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the value of ignore all causes option.
|
||||||
|
*
|
||||||
|
* @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed
|
||||||
|
* or converted to string will not include causes. If value is false causes will be appended to the stack trace of
|
||||||
|
* the main throwable.
|
||||||
|
*/
|
||||||
|
public static void setIgnoreAllCauses(final boolean ignoreAllCauses) {
|
||||||
|
AbstractExceptions.setIgnoreAllCauses(ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code>.
|
||||||
|
*
|
||||||
|
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
|
||||||
|
* by {@link #registerGroupPackage(String)} and ignore packages registered by
|
||||||
|
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code>.
|
||||||
|
*
|
||||||
|
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
|
||||||
|
* by {@link #registerGroupPackage(String)} and ignore packages registered by
|
||||||
|
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param groupPackageSet group packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackageSet root packages to use for building the stack trace
|
||||||
|
* @param groupPackageSet group packages to use for building the stack trace
|
||||||
|
* @param ignorePackageSet ignore packages to use for building the stack trace
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final boolean ignoreAllCauses) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackage root package to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final String rootPackage) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, rootPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param rootPackage root package to use for building the stack trace
|
||||||
|
* @param groupPackage group package to use for building the stack trace
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, rootPackage, groupPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be used
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final int maxDepth) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be used
|
||||||
|
* @param ignoreAllCauses ignore all causes in the exception chain
|
||||||
|
* @return a string containing the smart stack trace for the given <code>throwable</code>
|
||||||
|
*/
|
||||||
|
public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) {
|
||||||
|
return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns all messages found in the exception chain of the <code>throwable</code> as a single string.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return a string containing all messages found in the exception chain
|
||||||
|
*/
|
||||||
|
public static String getAllMessages(final Throwable throwable) {
|
||||||
|
return AbstractExceptions.getAllMessages(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns true if the given cause class is found in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @return true if the given cause class is found in the exception chain of the <code>throwable</code>, false
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass) {
|
||||||
|
return AbstractExceptions.containsCause(throwable, causeClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns true if the given cause class and cause message is found in the exception chain of the
|
||||||
|
* <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @return true if the given cause class and cause message is found in the exception chain of the
|
||||||
|
* <code>throwable</code>, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
|
||||||
|
return AbstractExceptions.containsCause(throwable, causeClass, causeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @return the cause of the <code>throwable</code> found or null if <code>throwable</code> does not have a cause
|
||||||
|
*/
|
||||||
|
public static Throwable getCause(final Throwable throwable) {
|
||||||
|
return AbstractExceptions.getCause(throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Note that this method walks through the exception chain up to given <code>maxDepth</code>. If exception chain
|
||||||
|
* includes more items than <code>maxDepth</code>, cause found at <code>maxDepth</code> level is returned.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the cause of the <code>throwable</code> found until <code>maxDepth</code> is reached in the exception
|
||||||
|
* chain or null if <code>throwable</code> does not have a cause
|
||||||
|
*/
|
||||||
|
public static Throwable getCause(final Throwable throwable, final int maxDepth) {
|
||||||
|
return AbstractExceptions.getCause(throwable, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass) {
|
||||||
|
return AbstractExceptions.searchCause(throwable, causeClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
|
||||||
|
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
* and cause message
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
|
||||||
|
return AbstractExceptions.searchCause(throwable, causeClass, causeMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
|
||||||
|
* <code>maxDepth</code>.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param causeMessage message to search for
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
* and cause message
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage, final int maxDepth) {
|
||||||
|
return AbstractExceptions.searchCause(throwable, causeClass, causeMessage, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
|
||||||
|
* <br>
|
||||||
|
* <br>
|
||||||
|
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
|
||||||
|
* <code>maxDepth</code>.
|
||||||
|
*
|
||||||
|
* @param throwable parent throwable
|
||||||
|
* @param causeClass class to search for
|
||||||
|
* @param maxDepth max depth in exception chain that will be searched
|
||||||
|
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
|
||||||
|
*/
|
||||||
|
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final int maxDepth) {
|
||||||
|
return AbstractExceptions.searchCause(throwable, causeClass, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -59,23 +59,27 @@ public class ArscBlamer {
|
|||||||
* Maps type chunk entries to blamed resources.
|
* Maps type chunk entries to blamed resources.
|
||||||
*/
|
*/
|
||||||
private final Multimap<TypeChunk.Entry, ResourceEntry> typeEntryToBlame = HashMultimap.create();
|
private final Multimap<TypeChunk.Entry, ResourceEntry> typeEntryToBlame = HashMultimap.create();
|
||||||
/**
|
|
||||||
* This is the {@link ResourceTableChunk} inside of the resources.arsc file in the APK.
|
|
||||||
*/
|
|
||||||
private final ResourceTableChunk resourceTable;
|
|
||||||
/**
|
/**
|
||||||
* Maps resources to the type chunk entries they reference.
|
* Maps resources to the type chunk entries they reference.
|
||||||
*/
|
*/
|
||||||
private Multimap<ResourceEntry, TypeChunk.Entry> resourceEntries;
|
private Multimap<ResourceEntry, TypeChunk.Entry> resourceEntries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps resources which have no base config to the type chunk entries they reference.
|
* Maps resources which have no base config to the type chunk entries they reference.
|
||||||
*/
|
*/
|
||||||
private Multimap<ResourceEntry, TypeChunk.Entry> baselessKeys;
|
private Multimap<ResourceEntry, TypeChunk.Entry> baselessKeys;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all of the type chunks in {@link #resourceTable}.
|
* Contains all of the type chunks in {@link #resourceTable}.
|
||||||
*/
|
*/
|
||||||
private List<TypeChunk> typeChunks;
|
private List<TypeChunk> typeChunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the {@link ResourceTableChunk} inside of the resources.arsc file in the APK.
|
||||||
|
*/
|
||||||
|
private final ResourceTableChunk resourceTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link ArscBlamer}.
|
* Creates a new {@link ArscBlamer}.
|
||||||
*
|
*
|
||||||
@@ -86,14 +90,6 @@ public class ArscBlamer {
|
|||||||
this.stringToBlame = createEntryListArray(resourceTable.getStringPool().getStringCount());
|
this.stringToBlame = createEntryListArray(resourceTable.getStringPool().getStringCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ResourceEntry>[] createEntryListArray(int size) {
|
|
||||||
ArrayListResourceEntry[] result = new ArrayListResourceEntry[size];
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
result[i] = new ArrayListResourceEntry();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates blame mappings.
|
* Generates blame mappings.
|
||||||
*/
|
*/
|
||||||
@@ -261,6 +257,14 @@ public class ArscBlamer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<ResourceEntry>[] createEntryListArray(int size) {
|
||||||
|
ArrayListResourceEntry[] result = new ArrayListResourceEntry[size];
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
result[i] = new ArrayListResourceEntry();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows creation of concrete parameterized type arr {@link ArscBlamer#createEntryListArray}.
|
* Allows creation of concrete parameterized type arr {@link ArscBlamer#createEntryListArray}.
|
||||||
*/
|
*/
|
||||||
@@ -279,12 +283,6 @@ public class ArscBlamer {
|
|||||||
private final String typeName;
|
private final String typeName;
|
||||||
private final String entryName;
|
private final String entryName;
|
||||||
|
|
||||||
private ResourceEntry(String packageName, String typeName, String entryName) {
|
|
||||||
this.packageName = packageName;
|
|
||||||
this.typeName = typeName;
|
|
||||||
this.entryName = entryName;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ResourceEntry create(TypeChunk.Entry entry) {
|
static ResourceEntry create(TypeChunk.Entry entry) {
|
||||||
PackageChunk packageChunk = Preconditions.checkNotNull(entry.parent().getPackageChunk());
|
PackageChunk packageChunk = Preconditions.checkNotNull(entry.parent().getPackageChunk());
|
||||||
String packageName = packageChunk.getPackageName();
|
String packageName = packageChunk.getPackageName();
|
||||||
@@ -293,6 +291,12 @@ public class ArscBlamer {
|
|||||||
return new ResourceEntry(packageName, typeName, entryName);
|
return new ResourceEntry(packageName, typeName, entryName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResourceEntry(String packageName, String typeName, String entryName) {
|
||||||
|
this.packageName = packageName;
|
||||||
|
this.typeName = typeName;
|
||||||
|
this.entryName = entryName;
|
||||||
|
}
|
||||||
|
|
||||||
public String packageName() {
|
public String packageName() {
|
||||||
return packageName;
|
return packageName;
|
||||||
}
|
}
|
||||||
|
|||||||
+240
-221
@@ -31,148 +31,6 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
|
|||||||
* Describes a particular resource configuration.
|
* Describes a particular resource configuration.
|
||||||
*/
|
*/
|
||||||
public class BinaryResourceConfiguration implements SerializableResource {
|
public class BinaryResourceConfiguration implements SerializableResource {
|
||||||
/**
|
|
||||||
* The below constants are from android.content.res.Configuration.
|
|
||||||
*/
|
|
||||||
private static final int DENSITY_DPI_UNDEFINED = 0;
|
|
||||||
private static final int DENSITY_DPI_LDPI = 120;
|
|
||||||
private static final int DENSITY_DPI_MDPI = 160;
|
|
||||||
private static final int DENSITY_DPI_TVDPI = 213;
|
|
||||||
private static final int DENSITY_DPI_HDPI = 240;
|
|
||||||
private static final int DENSITY_DPI_XHDPI = 320;
|
|
||||||
private static final int DENSITY_DPI_XXHDPI = 480;
|
|
||||||
private static final int DENSITY_DPI_XXXHDPI = 640;
|
|
||||||
private static final int DENSITY_DPI_ANY = 0xFFFE;
|
|
||||||
private static final int DENSITY_DPI_NONE = 0xFFFF;
|
|
||||||
private static final Map<Integer, String> DENSITY_DPI_VALUES =
|
|
||||||
ImmutableMap.<Integer, String>builder()
|
|
||||||
.put(DENSITY_DPI_UNDEFINED, "")
|
|
||||||
.put(DENSITY_DPI_LDPI, "ldpi")
|
|
||||||
.put(DENSITY_DPI_MDPI, "mdpi")
|
|
||||||
.put(DENSITY_DPI_TVDPI, "tvdpi")
|
|
||||||
.put(DENSITY_DPI_HDPI, "hdpi")
|
|
||||||
.put(DENSITY_DPI_XHDPI, "xhdpi")
|
|
||||||
.put(DENSITY_DPI_XXHDPI, "xxhdpi")
|
|
||||||
.put(DENSITY_DPI_XXXHDPI, "xxxhdpi")
|
|
||||||
.put(DENSITY_DPI_ANY, "anydpi")
|
|
||||||
.put(DENSITY_DPI_NONE, "nodpi")
|
|
||||||
.build();
|
|
||||||
private static final int KEYBOARD_NOKEYS = 1;
|
|
||||||
private static final int KEYBOARD_QWERTY = 2;
|
|
||||||
private static final int KEYBOARD_12KEY = 3;
|
|
||||||
private static final Map<Integer, String> KEYBOARD_VALUES = ImmutableMap.of(
|
|
||||||
KEYBOARD_NOKEYS, "nokeys",
|
|
||||||
KEYBOARD_QWERTY, "qwerty",
|
|
||||||
KEYBOARD_12KEY, "12key");
|
|
||||||
private static final int KEYBOARDHIDDEN_MASK = 0x03;
|
|
||||||
private static final int KEYBOARDHIDDEN_NO = 1;
|
|
||||||
private static final int KEYBOARDHIDDEN_YES = 2;
|
|
||||||
private static final int KEYBOARDHIDDEN_SOFT = 3;
|
|
||||||
private static final Map<Integer, String> KEYBOARDHIDDEN_VALUES = ImmutableMap.of(
|
|
||||||
KEYBOARDHIDDEN_NO, "keysexposed",
|
|
||||||
KEYBOARDHIDDEN_YES, "keyshidden",
|
|
||||||
KEYBOARDHIDDEN_SOFT, "keyssoft");
|
|
||||||
private static final int NAVIGATION_NONAV = 1;
|
|
||||||
private static final int NAVIGATION_DPAD = 2;
|
|
||||||
private static final int NAVIGATION_TRACKBALL = 3;
|
|
||||||
private static final int NAVIGATION_WHEEL = 4;
|
|
||||||
private static final Map<Integer, String> NAVIGATION_VALUES = ImmutableMap.of(
|
|
||||||
NAVIGATION_NONAV, "nonav",
|
|
||||||
NAVIGATION_DPAD, "dpad",
|
|
||||||
NAVIGATION_TRACKBALL, "trackball",
|
|
||||||
NAVIGATION_WHEEL, "wheel");
|
|
||||||
private static final int NAVIGATIONHIDDEN_MASK = 0x0C;
|
|
||||||
private static final int NAVIGATIONHIDDEN_NO = 0x04;
|
|
||||||
private static final int NAVIGATIONHIDDEN_YES = 0x08;
|
|
||||||
private static final Map<Integer, String> NAVIGATIONHIDDEN_VALUES = ImmutableMap.of(
|
|
||||||
NAVIGATIONHIDDEN_NO, "navexposed",
|
|
||||||
NAVIGATIONHIDDEN_YES, "navhidden");
|
|
||||||
private static final int ORIENTATION_PORTRAIT = 0x01;
|
|
||||||
private static final int ORIENTATION_LANDSCAPE = 0x02;
|
|
||||||
private static final Map<Integer, String> ORIENTATION_VALUES = ImmutableMap.of(
|
|
||||||
ORIENTATION_PORTRAIT, "port",
|
|
||||||
ORIENTATION_LANDSCAPE, "land");
|
|
||||||
private static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
|
|
||||||
private static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
|
|
||||||
private static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
|
|
||||||
private static final Map<Integer, String> SCREENLAYOUT_LAYOUTDIR_VALUES = ImmutableMap.of(
|
|
||||||
SCREENLAYOUT_LAYOUTDIR_LTR, "ldltr",
|
|
||||||
SCREENLAYOUT_LAYOUTDIR_RTL, "ldrtl");
|
|
||||||
private static final int SCREENLAYOUT_LONG_MASK = 0x30;
|
|
||||||
private static final int SCREENLAYOUT_LONG_NO = 0x10;
|
|
||||||
private static final int SCREENLAYOUT_LONG_YES = 0x20;
|
|
||||||
private static final Map<Integer, String> SCREENLAYOUT_LONG_VALUES = ImmutableMap.of(
|
|
||||||
SCREENLAYOUT_LONG_NO, "notlong",
|
|
||||||
SCREENLAYOUT_LONG_YES, "long");
|
|
||||||
private static final int SCREENLAYOUT_ROUND_MASK = 0x03;
|
|
||||||
private static final int SCREENLAYOUT_ROUND_NO = 0x01;
|
|
||||||
private static final int SCREENLAYOUT_ROUND_YES = 0x02;
|
|
||||||
private static final Map<Integer, String> SCREENLAYOUT_ROUND_VALUES = ImmutableMap.of(
|
|
||||||
SCREENLAYOUT_ROUND_NO, "notround",
|
|
||||||
SCREENLAYOUT_ROUND_YES, "round");
|
|
||||||
private static final int SCREENLAYOUT_SIZE_MASK = 0x0F;
|
|
||||||
private static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
|
|
||||||
private static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
|
|
||||||
private static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
|
|
||||||
private static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
|
|
||||||
private static final Map<Integer, String> SCREENLAYOUT_SIZE_VALUES = ImmutableMap.of(
|
|
||||||
SCREENLAYOUT_SIZE_SMALL, "small",
|
|
||||||
SCREENLAYOUT_SIZE_NORMAL, "normal",
|
|
||||||
SCREENLAYOUT_SIZE_LARGE, "large",
|
|
||||||
SCREENLAYOUT_SIZE_XLARGE, "xlarge");
|
|
||||||
private static final int TOUCHSCREEN_NOTOUCH = 1;
|
|
||||||
private static final int TOUCHSCREEN_FINGER = 3;
|
|
||||||
private static final Map<Integer, String> TOUCHSCREEN_VALUES = ImmutableMap.of(
|
|
||||||
TOUCHSCREEN_NOTOUCH, "notouch",
|
|
||||||
TOUCHSCREEN_FINGER, "finger");
|
|
||||||
private static final int UI_MODE_NIGHT_MASK = 0x30;
|
|
||||||
private static final int UI_MODE_NIGHT_NO = 0x10;
|
|
||||||
private static final int UI_MODE_NIGHT_YES = 0x20;
|
|
||||||
private static final Map<Integer, String> UI_MODE_NIGHT_VALUES = ImmutableMap.of(
|
|
||||||
UI_MODE_NIGHT_NO, "notnight",
|
|
||||||
UI_MODE_NIGHT_YES, "night");
|
|
||||||
private static final int UI_MODE_TYPE_MASK = 0x0F;
|
|
||||||
private static final int UI_MODE_TYPE_DESK = 0x02;
|
|
||||||
private static final int UI_MODE_TYPE_CAR = 0x03;
|
|
||||||
private static final int UI_MODE_TYPE_TELEVISION = 0x04;
|
|
||||||
private static final int UI_MODE_TYPE_APPLIANCE = 0x05;
|
|
||||||
private static final int UI_MODE_TYPE_WATCH = 0x06;
|
|
||||||
private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
|
|
||||||
// start - miui
|
|
||||||
private static final int UI_MODE_TYPE_GODZILLAUI = 0x0b;
|
|
||||||
private static final int UI_MODE_TYPE_SMALLUI = 0x0c;
|
|
||||||
private static final int UI_MODE_TYPE_MEDIUMUI = 0x0d;
|
|
||||||
private static final int UI_MODE_TYPE_LARGEUI = 0x0e;
|
|
||||||
private static final int UI_MODE_TYPE_HUGEUI = 0x0f;
|
|
||||||
private static final Map<Integer, String> UI_MODE_TYPE_VALUES =
|
|
||||||
ImmutableMap.<Integer, String>builder()
|
|
||||||
.put(UI_MODE_TYPE_DESK, "desk")
|
|
||||||
.put(UI_MODE_TYPE_CAR, "car")
|
|
||||||
.put(UI_MODE_TYPE_TELEVISION, "television")
|
|
||||||
.put(UI_MODE_TYPE_APPLIANCE, "appliance")
|
|
||||||
.put(UI_MODE_TYPE_WATCH, "watch")
|
|
||||||
.put(UI_MODE_TYPE_VR_HEADSET, "vrheadset")
|
|
||||||
.put(UI_MODE_TYPE_GODZILLAUI, "godzillaui")
|
|
||||||
.put(UI_MODE_TYPE_SMALLUI, "smallui")
|
|
||||||
.put(UI_MODE_TYPE_MEDIUMUI, "mediumui")
|
|
||||||
.put(UI_MODE_TYPE_LARGEUI, "largeui")
|
|
||||||
.put(UI_MODE_TYPE_HUGEUI, "hugeui").build();
|
|
||||||
/**
|
|
||||||
* The minimum size in bytes that this configuration must be to contain screen config info.
|
|
||||||
*/
|
|
||||||
private static final int SCREEN_CONFIG_MIN_SIZE = 32;
|
|
||||||
/**
|
|
||||||
* The minimum size in bytes that this configuration must be to contain screen dp info.
|
|
||||||
*/
|
|
||||||
private static final int SCREEN_DP_MIN_SIZE = 36;
|
|
||||||
/**
|
|
||||||
* The minimum size in bytes that this configuration must be to contain locale info.
|
|
||||||
*/
|
|
||||||
private static final int LOCALE_MIN_SIZE = 48;
|
|
||||||
/**
|
|
||||||
* The minimum size in bytes that this config must be to contain the screenConfig extension.
|
|
||||||
*/
|
|
||||||
private static final int SCREEN_CONFIG_EXTENSION_MIN_SIZE = 52;
|
|
||||||
private final int size;
|
private final int size;
|
||||||
private final int mcc;
|
private final int mcc;
|
||||||
private final int mnc;
|
private final int mnc;
|
||||||
@@ -195,60 +53,9 @@ public class BinaryResourceConfiguration implements SerializableResource {
|
|||||||
private final int screenHeightDp;
|
private final int screenHeightDp;
|
||||||
private final byte[] localeScript;
|
private final byte[] localeScript;
|
||||||
private final byte[] localeVariant;
|
private final byte[] localeVariant;
|
||||||
// end - miui
|
|
||||||
private final int screenLayout2;
|
private final int screenLayout2;
|
||||||
private final byte[] unknown;
|
private final byte[] unknown;
|
||||||
|
|
||||||
private BinaryResourceConfiguration(int size,
|
|
||||||
int mcc,
|
|
||||||
int mnc,
|
|
||||||
byte[] language,
|
|
||||||
byte[] region,
|
|
||||||
int orientation,
|
|
||||||
int touchscreen,
|
|
||||||
int density,
|
|
||||||
int keyboard,
|
|
||||||
int navigation,
|
|
||||||
int inputFlags,
|
|
||||||
int screenWidth,
|
|
||||||
int screenHeight,
|
|
||||||
int sdkVersion,
|
|
||||||
int minorVersion,
|
|
||||||
int screenLayout,
|
|
||||||
int uiMode,
|
|
||||||
int smallestScreenWidthDp,
|
|
||||||
int screenWidthDp,
|
|
||||||
int screenHeightDp,
|
|
||||||
byte[] localeScript,
|
|
||||||
byte[] localeVariant,
|
|
||||||
int screenLayout2,
|
|
||||||
byte[] unknown) {
|
|
||||||
this.size = size;
|
|
||||||
this.mcc = mcc;
|
|
||||||
this.mnc = mnc;
|
|
||||||
this.language = language;
|
|
||||||
this.region = region;
|
|
||||||
this.orientation = orientation;
|
|
||||||
this.touchscreen = touchscreen;
|
|
||||||
this.density = density;
|
|
||||||
this.keyboard = keyboard;
|
|
||||||
this.navigation = navigation;
|
|
||||||
this.inputFlags = inputFlags;
|
|
||||||
this.screenWidth = screenWidth;
|
|
||||||
this.screenHeight = screenHeight;
|
|
||||||
this.sdkVersion = sdkVersion;
|
|
||||||
this.minorVersion = minorVersion;
|
|
||||||
this.screenLayout = screenLayout;
|
|
||||||
this.uiMode = uiMode;
|
|
||||||
this.smallestScreenWidthDp = smallestScreenWidthDp;
|
|
||||||
this.screenWidthDp = screenWidthDp;
|
|
||||||
this.screenHeightDp = screenHeightDp;
|
|
||||||
this.localeScript = localeScript;
|
|
||||||
this.localeVariant = localeVariant;
|
|
||||||
this.screenLayout2 = screenLayout2;
|
|
||||||
this.unknown = unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BinaryResourceConfiguration create(ByteBuffer buffer) {
|
static BinaryResourceConfiguration create(ByteBuffer buffer) {
|
||||||
int startPosition = buffer.position(); // The starting buffer position to calculate bytes read.
|
int startPosition = buffer.position(); // The starting buffer position to calculate bytes read.
|
||||||
int size = buffer.getInt();
|
int size = buffer.getInt();
|
||||||
@@ -314,6 +121,245 @@ public class BinaryResourceConfiguration implements SerializableResource {
|
|||||||
screenHeightDp, localeScript, localeVariant, screenLayout2, unknown);
|
screenHeightDp, localeScript, localeVariant, screenLayout2, unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BinaryResourceConfiguration(int size,
|
||||||
|
int mcc,
|
||||||
|
int mnc,
|
||||||
|
byte[] language,
|
||||||
|
byte[] region,
|
||||||
|
int orientation,
|
||||||
|
int touchscreen,
|
||||||
|
int density,
|
||||||
|
int keyboard,
|
||||||
|
int navigation,
|
||||||
|
int inputFlags,
|
||||||
|
int screenWidth,
|
||||||
|
int screenHeight,
|
||||||
|
int sdkVersion,
|
||||||
|
int minorVersion,
|
||||||
|
int screenLayout,
|
||||||
|
int uiMode,
|
||||||
|
int smallestScreenWidthDp,
|
||||||
|
int screenWidthDp,
|
||||||
|
int screenHeightDp,
|
||||||
|
byte[] localeScript,
|
||||||
|
byte[] localeVariant,
|
||||||
|
int screenLayout2,
|
||||||
|
byte[] unknown) {
|
||||||
|
this.size = size;
|
||||||
|
this.mcc = mcc;
|
||||||
|
this.mnc = mnc;
|
||||||
|
this.language = language;
|
||||||
|
this.region = region;
|
||||||
|
this.orientation = orientation;
|
||||||
|
this.touchscreen = touchscreen;
|
||||||
|
this.density = density;
|
||||||
|
this.keyboard = keyboard;
|
||||||
|
this.navigation = navigation;
|
||||||
|
this.inputFlags = inputFlags;
|
||||||
|
this.screenWidth = screenWidth;
|
||||||
|
this.screenHeight = screenHeight;
|
||||||
|
this.sdkVersion = sdkVersion;
|
||||||
|
this.minorVersion = minorVersion;
|
||||||
|
this.screenLayout = screenLayout;
|
||||||
|
this.uiMode = uiMode;
|
||||||
|
this.smallestScreenWidthDp = smallestScreenWidthDp;
|
||||||
|
this.screenWidthDp = screenWidthDp;
|
||||||
|
this.screenHeightDp = screenHeightDp;
|
||||||
|
this.localeScript = localeScript;
|
||||||
|
this.localeVariant = localeVariant;
|
||||||
|
this.screenLayout2 = screenLayout2;
|
||||||
|
this.unknown = unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different types of configs that can be present in a {@link BinaryResourceConfiguration}.
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
MCC,
|
||||||
|
MNC,
|
||||||
|
LANGUAGE_STRING,
|
||||||
|
REGION_STRING,
|
||||||
|
SCREEN_LAYOUT_DIRECTION,
|
||||||
|
SMALLEST_SCREEN_WIDTH_DP,
|
||||||
|
SCREEN_WIDTH_DP,
|
||||||
|
SCREEN_HEIGHT_DP,
|
||||||
|
SCREEN_LAYOUT_SIZE,
|
||||||
|
SCREEN_LAYOUT_LONG,
|
||||||
|
SCREEN_LAYOUT_ROUND,
|
||||||
|
ORIENTATION,
|
||||||
|
UI_MODE_TYPE,
|
||||||
|
UI_MODE_NIGHT,
|
||||||
|
DENSITY_DPI,
|
||||||
|
TOUCHSCREEN,
|
||||||
|
KEYBOARD_HIDDEN,
|
||||||
|
KEYBOARD,
|
||||||
|
NAVIGATION_HIDDEN,
|
||||||
|
NAVIGATION,
|
||||||
|
SDK_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The below constants are from android.content.res.Configuration.
|
||||||
|
*/
|
||||||
|
private static final int DENSITY_DPI_UNDEFINED = 0;
|
||||||
|
private static final int DENSITY_DPI_LDPI = 120;
|
||||||
|
private static final int DENSITY_DPI_MDPI = 160;
|
||||||
|
private static final int DENSITY_DPI_TVDPI = 213;
|
||||||
|
private static final int DENSITY_DPI_HDPI = 240;
|
||||||
|
private static final int DENSITY_DPI_XHDPI = 320;
|
||||||
|
private static final int DENSITY_DPI_XXHDPI = 480;
|
||||||
|
private static final int DENSITY_DPI_XXXHDPI = 640;
|
||||||
|
private static final int DENSITY_DPI_ANY = 0xFFFE;
|
||||||
|
private static final int DENSITY_DPI_NONE = 0xFFFF;
|
||||||
|
private static final Map<Integer, String> DENSITY_DPI_VALUES =
|
||||||
|
ImmutableMap.<Integer, String>builder()
|
||||||
|
.put(DENSITY_DPI_UNDEFINED, "")
|
||||||
|
.put(DENSITY_DPI_LDPI, "ldpi")
|
||||||
|
.put(DENSITY_DPI_MDPI, "mdpi")
|
||||||
|
.put(DENSITY_DPI_TVDPI, "tvdpi")
|
||||||
|
.put(DENSITY_DPI_HDPI, "hdpi")
|
||||||
|
.put(DENSITY_DPI_XHDPI, "xhdpi")
|
||||||
|
.put(DENSITY_DPI_XXHDPI, "xxhdpi")
|
||||||
|
.put(DENSITY_DPI_XXXHDPI, "xxxhdpi")
|
||||||
|
.put(DENSITY_DPI_ANY, "anydpi")
|
||||||
|
.put(DENSITY_DPI_NONE, "nodpi")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final int KEYBOARD_NOKEYS = 1;
|
||||||
|
private static final int KEYBOARD_QWERTY = 2;
|
||||||
|
private static final int KEYBOARD_12KEY = 3;
|
||||||
|
private static final Map<Integer, String> KEYBOARD_VALUES = ImmutableMap.of(
|
||||||
|
KEYBOARD_NOKEYS, "nokeys",
|
||||||
|
KEYBOARD_QWERTY, "qwerty",
|
||||||
|
KEYBOARD_12KEY, "12key");
|
||||||
|
|
||||||
|
private static final int KEYBOARDHIDDEN_MASK = 0x03;
|
||||||
|
private static final int KEYBOARDHIDDEN_NO = 1;
|
||||||
|
private static final int KEYBOARDHIDDEN_YES = 2;
|
||||||
|
private static final int KEYBOARDHIDDEN_SOFT = 3;
|
||||||
|
private static final Map<Integer, String> KEYBOARDHIDDEN_VALUES = ImmutableMap.of(
|
||||||
|
KEYBOARDHIDDEN_NO, "keysexposed",
|
||||||
|
KEYBOARDHIDDEN_YES, "keyshidden",
|
||||||
|
KEYBOARDHIDDEN_SOFT, "keyssoft");
|
||||||
|
|
||||||
|
private static final int NAVIGATION_NONAV = 1;
|
||||||
|
private static final int NAVIGATION_DPAD = 2;
|
||||||
|
private static final int NAVIGATION_TRACKBALL = 3;
|
||||||
|
private static final int NAVIGATION_WHEEL = 4;
|
||||||
|
private static final Map<Integer, String> NAVIGATION_VALUES = ImmutableMap.of(
|
||||||
|
NAVIGATION_NONAV, "nonav",
|
||||||
|
NAVIGATION_DPAD, "dpad",
|
||||||
|
NAVIGATION_TRACKBALL, "trackball",
|
||||||
|
NAVIGATION_WHEEL, "wheel");
|
||||||
|
|
||||||
|
private static final int NAVIGATIONHIDDEN_MASK = 0x0C;
|
||||||
|
private static final int NAVIGATIONHIDDEN_NO = 0x04;
|
||||||
|
private static final int NAVIGATIONHIDDEN_YES = 0x08;
|
||||||
|
private static final Map<Integer, String> NAVIGATIONHIDDEN_VALUES = ImmutableMap.of(
|
||||||
|
NAVIGATIONHIDDEN_NO, "navexposed",
|
||||||
|
NAVIGATIONHIDDEN_YES, "navhidden");
|
||||||
|
|
||||||
|
private static final int ORIENTATION_PORTRAIT = 0x01;
|
||||||
|
private static final int ORIENTATION_LANDSCAPE = 0x02;
|
||||||
|
private static final Map<Integer, String> ORIENTATION_VALUES = ImmutableMap.of(
|
||||||
|
ORIENTATION_PORTRAIT, "port",
|
||||||
|
ORIENTATION_LANDSCAPE, "land");
|
||||||
|
|
||||||
|
private static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
|
||||||
|
private static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
|
||||||
|
private static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
|
||||||
|
private static final Map<Integer, String> SCREENLAYOUT_LAYOUTDIR_VALUES = ImmutableMap.of(
|
||||||
|
SCREENLAYOUT_LAYOUTDIR_LTR, "ldltr",
|
||||||
|
SCREENLAYOUT_LAYOUTDIR_RTL, "ldrtl");
|
||||||
|
|
||||||
|
private static final int SCREENLAYOUT_LONG_MASK = 0x30;
|
||||||
|
private static final int SCREENLAYOUT_LONG_NO = 0x10;
|
||||||
|
private static final int SCREENLAYOUT_LONG_YES = 0x20;
|
||||||
|
private static final Map<Integer, String> SCREENLAYOUT_LONG_VALUES = ImmutableMap.of(
|
||||||
|
SCREENLAYOUT_LONG_NO, "notlong",
|
||||||
|
SCREENLAYOUT_LONG_YES, "long");
|
||||||
|
|
||||||
|
private static final int SCREENLAYOUT_ROUND_MASK = 0x03;
|
||||||
|
private static final int SCREENLAYOUT_ROUND_NO = 0x01;
|
||||||
|
private static final int SCREENLAYOUT_ROUND_YES = 0x02;
|
||||||
|
private static final Map<Integer, String> SCREENLAYOUT_ROUND_VALUES = ImmutableMap.of(
|
||||||
|
SCREENLAYOUT_ROUND_NO, "notround",
|
||||||
|
SCREENLAYOUT_ROUND_YES, "round");
|
||||||
|
|
||||||
|
private static final int SCREENLAYOUT_SIZE_MASK = 0x0F;
|
||||||
|
private static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
|
||||||
|
private static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
|
||||||
|
private static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
|
||||||
|
private static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
|
||||||
|
private static final Map<Integer, String> SCREENLAYOUT_SIZE_VALUES = ImmutableMap.of(
|
||||||
|
SCREENLAYOUT_SIZE_SMALL, "small",
|
||||||
|
SCREENLAYOUT_SIZE_NORMAL, "normal",
|
||||||
|
SCREENLAYOUT_SIZE_LARGE, "large",
|
||||||
|
SCREENLAYOUT_SIZE_XLARGE, "xlarge");
|
||||||
|
|
||||||
|
private static final int TOUCHSCREEN_NOTOUCH = 1;
|
||||||
|
private static final int TOUCHSCREEN_FINGER = 3;
|
||||||
|
private static final Map<Integer, String> TOUCHSCREEN_VALUES = ImmutableMap.of(
|
||||||
|
TOUCHSCREEN_NOTOUCH, "notouch",
|
||||||
|
TOUCHSCREEN_FINGER, "finger");
|
||||||
|
|
||||||
|
private static final int UI_MODE_NIGHT_MASK = 0x30;
|
||||||
|
private static final int UI_MODE_NIGHT_NO = 0x10;
|
||||||
|
private static final int UI_MODE_NIGHT_YES = 0x20;
|
||||||
|
private static final Map<Integer, String> UI_MODE_NIGHT_VALUES = ImmutableMap.of(
|
||||||
|
UI_MODE_NIGHT_NO, "notnight",
|
||||||
|
UI_MODE_NIGHT_YES, "night");
|
||||||
|
|
||||||
|
private static final int UI_MODE_TYPE_MASK = 0x0F;
|
||||||
|
private static final int UI_MODE_TYPE_DESK = 0x02;
|
||||||
|
private static final int UI_MODE_TYPE_CAR = 0x03;
|
||||||
|
private static final int UI_MODE_TYPE_TELEVISION = 0x04;
|
||||||
|
private static final int UI_MODE_TYPE_APPLIANCE = 0x05;
|
||||||
|
private static final int UI_MODE_TYPE_WATCH = 0x06;
|
||||||
|
private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
|
||||||
|
|
||||||
|
// start - miui
|
||||||
|
private static final int UI_MODE_TYPE_GODZILLAUI = 0x0b;
|
||||||
|
private static final int UI_MODE_TYPE_SMALLUI = 0x0c;
|
||||||
|
private static final int UI_MODE_TYPE_MEDIUMUI = 0x0d;
|
||||||
|
private static final int UI_MODE_TYPE_LARGEUI = 0x0e;
|
||||||
|
private static final int UI_MODE_TYPE_HUGEUI = 0x0f;
|
||||||
|
// end - miui
|
||||||
|
|
||||||
|
private static final Map<Integer, String> UI_MODE_TYPE_VALUES =
|
||||||
|
ImmutableMap.<Integer, String>builder()
|
||||||
|
.put(UI_MODE_TYPE_DESK, "desk")
|
||||||
|
.put(UI_MODE_TYPE_CAR, "car")
|
||||||
|
.put(UI_MODE_TYPE_TELEVISION, "television")
|
||||||
|
.put(UI_MODE_TYPE_APPLIANCE, "appliance")
|
||||||
|
.put(UI_MODE_TYPE_WATCH, "watch")
|
||||||
|
.put(UI_MODE_TYPE_VR_HEADSET, "vrheadset")
|
||||||
|
.put(UI_MODE_TYPE_GODZILLAUI, "godzillaui")
|
||||||
|
.put(UI_MODE_TYPE_SMALLUI, "smallui")
|
||||||
|
.put(UI_MODE_TYPE_MEDIUMUI, "mediumui")
|
||||||
|
.put(UI_MODE_TYPE_LARGEUI, "largeui")
|
||||||
|
.put(UI_MODE_TYPE_HUGEUI, "hugeui").build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum size in bytes that this configuration must be to contain screen config info.
|
||||||
|
*/
|
||||||
|
private static final int SCREEN_CONFIG_MIN_SIZE = 32;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum size in bytes that this configuration must be to contain screen dp info.
|
||||||
|
*/
|
||||||
|
private static final int SCREEN_DP_MIN_SIZE = 36;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum size in bytes that this configuration must be to contain locale info.
|
||||||
|
*/
|
||||||
|
private static final int LOCALE_MIN_SIZE = 48;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum size in bytes that this config must be to contain the screenConfig extension.
|
||||||
|
*/
|
||||||
|
private static final int SCREEN_CONFIG_EXTENSION_MIN_SIZE = 52;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of bytes that this resource configuration takes up.
|
* The number of bytes that this resource configuration takes up.
|
||||||
*/
|
*/
|
||||||
@@ -662,7 +708,7 @@ public class BinaryResourceConfiguration implements SerializableResource {
|
|||||||
Map<Type, String> result = new LinkedHashMap<>(); // Preserve order for #toString().
|
Map<Type, String> result = new LinkedHashMap<>(); // Preserve order for #toString().
|
||||||
result.put(Type.MCC, mcc() != 0 ? "mcc" + mcc() : "");
|
result.put(Type.MCC, mcc() != 0 ? "mcc" + mcc() : "");
|
||||||
result.put(Type.MNC, mnc() != 0 ? "mnc" + mnc() : "");
|
result.put(Type.MNC, mnc() != 0 ? "mnc" + mnc() : "");
|
||||||
result.put(Type.LANGUAGE_STRING, !languageString().isEmpty() ? languageString() : "");
|
result.put(Type.LANGUAGE_STRING, !languageString().isEmpty() ? "" + languageString() : "");
|
||||||
result.put(Type.REGION_STRING, !regionString().isEmpty() ? "r" + regionString() : "");
|
result.put(Type.REGION_STRING, !regionString().isEmpty() ? "r" + regionString() : "");
|
||||||
result.put(Type.SCREEN_LAYOUT_DIRECTION,
|
result.put(Type.SCREEN_LAYOUT_DIRECTION,
|
||||||
getOrDefault(SCREENLAYOUT_LAYOUTDIR_VALUES, screenLayoutDirection(), ""));
|
getOrDefault(SCREENLAYOUT_LAYOUTDIR_VALUES, screenLayoutDirection(), ""));
|
||||||
@@ -696,31 +742,4 @@ public class BinaryResourceConfiguration implements SerializableResource {
|
|||||||
V value = map.get(key);
|
V value = map.get(key);
|
||||||
return value != null ? value : defaultValue;
|
return value != null ? value : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The different types of configs that can be present in a {@link BinaryResourceConfiguration}.
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
MCC,
|
|
||||||
MNC,
|
|
||||||
LANGUAGE_STRING,
|
|
||||||
REGION_STRING,
|
|
||||||
SCREEN_LAYOUT_DIRECTION,
|
|
||||||
SMALLEST_SCREEN_WIDTH_DP,
|
|
||||||
SCREEN_WIDTH_DP,
|
|
||||||
SCREEN_HEIGHT_DP,
|
|
||||||
SCREEN_LAYOUT_SIZE,
|
|
||||||
SCREEN_LAYOUT_LONG,
|
|
||||||
SCREEN_LAYOUT_ROUND,
|
|
||||||
ORIENTATION,
|
|
||||||
UI_MODE_TYPE,
|
|
||||||
UI_MODE_NIGHT,
|
|
||||||
DENSITY_DPI,
|
|
||||||
TOUCHSCREEN,
|
|
||||||
KEYBOARD_HIDDEN,
|
|
||||||
KEYBOARD,
|
|
||||||
NAVIGATION_HIDDEN,
|
|
||||||
NAVIGATION,
|
|
||||||
SDK_VERSION
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -49,12 +49,6 @@ public class BinaryResourceIdentifier {
|
|||||||
private final int typeId;
|
private final int typeId;
|
||||||
private final int entryId;
|
private final int entryId;
|
||||||
|
|
||||||
private BinaryResourceIdentifier(int packageId, int typeId, int entryId) {
|
|
||||||
this.packageId = packageId;
|
|
||||||
this.typeId = typeId;
|
|
||||||
this.entryId = entryId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link BinaryResourceIdentifier} from a {@code resourceId} of the form 0xpptteeee.
|
* Returns a {@link BinaryResourceIdentifier} from a {@code resourceId} of the form 0xpptteeee.
|
||||||
*/
|
*/
|
||||||
@@ -75,6 +69,12 @@ public class BinaryResourceIdentifier {
|
|||||||
return new BinaryResourceIdentifier(packageId, typeId, entryId);
|
return new BinaryResourceIdentifier(packageId, typeId, entryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BinaryResourceIdentifier(int packageId, int typeId, int entryId) {
|
||||||
|
this.packageId = packageId;
|
||||||
|
this.typeId = typeId;
|
||||||
|
this.entryId = entryId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The (1-based) id of the {@link PackageChunk} containing this resource.
|
* The (1-based) id of the {@link PackageChunk} containing this resource.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+18
-18
@@ -31,6 +31,24 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||||||
*/
|
*/
|
||||||
public final class BinaryResourceString {
|
public final class BinaryResourceString {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of {@link BinaryResourceString} to encode / decode.
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
UTF8(UTF_8),
|
||||||
|
UTF16(UTF_16LE);
|
||||||
|
|
||||||
|
private final Charset charset;
|
||||||
|
|
||||||
|
Type(Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Charset charset() {
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private BinaryResourceString() {
|
private BinaryResourceString() {
|
||||||
} // Private constructor
|
} // Private constructor
|
||||||
|
|
||||||
@@ -148,22 +166,4 @@ public final class BinaryResourceString {
|
|||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of {@link BinaryResourceString} to encode / decode.
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
UTF8(UTF_8),
|
|
||||||
UTF16(UTF_16LE);
|
|
||||||
|
|
||||||
private final Charset charset;
|
|
||||||
|
|
||||||
Type(Charset charset) {
|
|
||||||
this.charset = charset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Charset charset() {
|
|
||||||
return charset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-78
@@ -31,78 +31,6 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class BinaryResourceValue implements SerializableResource {
|
public class BinaryResourceValue implements SerializableResource {
|
||||||
|
|
||||||
/**
|
|
||||||
* The serialized size in bytes of a {@link BinaryResourceValue}.
|
|
||||||
*/
|
|
||||||
public static final int SIZE = 8;
|
|
||||||
public int size;
|
|
||||||
public Type type;
|
|
||||||
public int data;
|
|
||||||
public BinaryResourceValue(int size, Type type, int data) {
|
|
||||||
this.size = size;
|
|
||||||
this.type = type;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BinaryResourceValue create(ByteBuffer buffer) {
|
|
||||||
int size = (buffer.getShort() & 0xFFFF);
|
|
||||||
buffer.get(); // Unused
|
|
||||||
Type type = Type.fromCode(buffer.get());
|
|
||||||
int data = buffer.getInt();
|
|
||||||
return new BinaryResourceValue(size, type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length in bytes of this value.
|
|
||||||
*/
|
|
||||||
public int size() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The raw data type of this value.
|
|
||||||
*/
|
|
||||||
public Type type() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The actual 4-byte value; interpretation of the value depends on {@code dataType}.
|
|
||||||
*/
|
|
||||||
public int data() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] toByteArray() {
|
|
||||||
return toByteArray(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] toByteArray(boolean shrink) {
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
buffer.putShort((short) size());
|
|
||||||
buffer.put((byte) 0); // Unused
|
|
||||||
buffer.put(type().code());
|
|
||||||
buffer.putInt(data());
|
|
||||||
return buffer.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
BinaryResourceValue that = (BinaryResourceValue) o;
|
|
||||||
return size == that.size &&
|
|
||||||
data == that.data &&
|
|
||||||
type == that.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(size, type, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource type codes.
|
* Resource type codes.
|
||||||
*/
|
*/
|
||||||
@@ -172,6 +100,8 @@ public class BinaryResourceValue implements SerializableResource {
|
|||||||
*/
|
*/
|
||||||
INT_COLOR_RGB4(0x1f);
|
INT_COLOR_RGB4(0x1f);
|
||||||
|
|
||||||
|
private final byte code;
|
||||||
|
|
||||||
private static final Map<Byte, Type> FROM_BYTE;
|
private static final Map<Byte, Type> FROM_BYTE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -182,18 +112,90 @@ public class BinaryResourceValue implements SerializableResource {
|
|||||||
FROM_BYTE = builder.build();
|
FROM_BYTE = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final byte code;
|
|
||||||
|
|
||||||
Type(int code) {
|
Type(int code) {
|
||||||
this.code = UnsignedBytes.checkedCast(code);
|
this.code = UnsignedBytes.checkedCast(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type fromCode(byte code) {
|
|
||||||
return Preconditions.checkNotNull(FROM_BYTE.get(code), "Unknown resource type: %s", code);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte code() {
|
public byte code() {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type fromCode(byte code) {
|
||||||
|
return Preconditions.checkNotNull(FROM_BYTE.get(code), "Unknown resource type: %s", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The serialized size in bytes of a {@link BinaryResourceValue}.
|
||||||
|
*/
|
||||||
|
public static final int SIZE = 8;
|
||||||
|
|
||||||
|
public int size;
|
||||||
|
public Type type;
|
||||||
|
public int data;
|
||||||
|
|
||||||
|
public static BinaryResourceValue create(ByteBuffer buffer) {
|
||||||
|
int size = (buffer.getShort() & 0xFFFF);
|
||||||
|
buffer.get(); // Unused
|
||||||
|
Type type = Type.fromCode(buffer.get());
|
||||||
|
int data = buffer.getInt();
|
||||||
|
return new BinaryResourceValue(size, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryResourceValue(int size, Type type, int data) {
|
||||||
|
this.size = size;
|
||||||
|
this.type = type;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length in bytes of this value.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raw data type of this value.
|
||||||
|
*/
|
||||||
|
public Type type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual 4-byte value; interpretation of the value depends on {@code dataType}.
|
||||||
|
*/
|
||||||
|
public int data() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
return toByteArray(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] toByteArray(boolean shrink) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(SIZE).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
buffer.putShort((short) size());
|
||||||
|
buffer.put((byte) 0); // Unused
|
||||||
|
buffer.put(type().code());
|
||||||
|
buffer.putInt(data());
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
BinaryResourceValue that = (BinaryResourceValue) o;
|
||||||
|
return size == that.size &&
|
||||||
|
data == that.data &&
|
||||||
|
type == that.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(size, type, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,36 +35,86 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public abstract class Chunk implements SerializableResource {
|
public abstract class Chunk implements SerializableResource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types of chunks that can exist.
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
NULL(0x0000),
|
||||||
|
STRING_POOL(0x0001),
|
||||||
|
TABLE(0x0002),
|
||||||
|
XML(0x0003),
|
||||||
|
XML_START_NAMESPACE(0x0100),
|
||||||
|
XML_END_NAMESPACE(0x0101),
|
||||||
|
XML_START_ELEMENT(0x0102),
|
||||||
|
XML_END_ELEMENT(0x0103),
|
||||||
|
XML_CDATA(0x0104),
|
||||||
|
XML_RESOURCE_MAP(0x0180),
|
||||||
|
TABLE_PACKAGE(0x0200),
|
||||||
|
TABLE_TYPE(0x0201),
|
||||||
|
TABLE_TYPE_SPEC(0x0202),
|
||||||
|
TABLE_LIBRARY(0x0203);
|
||||||
|
|
||||||
|
private final short code;
|
||||||
|
|
||||||
|
private static final Map<Short, Type> FROM_SHORT;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Builder<Short, Type> builder = ImmutableMap.builder();
|
||||||
|
for (Type type : values()) {
|
||||||
|
builder.put(type.code(), type);
|
||||||
|
}
|
||||||
|
FROM_SHORT = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Type(int code) {
|
||||||
|
this.code = Shorts.checkedCast(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short code() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type fromCode(short code) {
|
||||||
|
return Preconditions.checkNotNull(FROM_SHORT.get(code), "Unknown chunk type: %s", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The byte boundary to pad chunks on.
|
* The byte boundary to pad chunks on.
|
||||||
*/
|
*/
|
||||||
public static final int PAD_BOUNDARY = 4;
|
public static final int PAD_BOUNDARY = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of bytes in every chunk that describes chunk type, header size, and chunk size.
|
* The number of bytes in every chunk that describes chunk type, header size, and chunk size.
|
||||||
*/
|
*/
|
||||||
public static final int METADATA_SIZE = 8;
|
public static final int METADATA_SIZE = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The offset in bytes, from the start of the chunk, where the chunk size can be found.
|
* The offset in bytes, from the start of the chunk, where the chunk size can be found.
|
||||||
*/
|
*/
|
||||||
private static final int CHUNK_SIZE_OFFSET = 4;
|
private static final int CHUNK_SIZE_OFFSET = 4;
|
||||||
/**
|
|
||||||
* Size of the chunk header in bytes.
|
|
||||||
*/
|
|
||||||
protected final int headerSize;
|
|
||||||
/**
|
|
||||||
* headerSize + dataSize. The total size of this chunk.
|
|
||||||
*/
|
|
||||||
protected final int chunkSize;
|
|
||||||
/**
|
|
||||||
* Offset of this chunk from the start of the file.
|
|
||||||
*/
|
|
||||||
protected final int offset;
|
|
||||||
/**
|
/**
|
||||||
* The parent to this chunk, if any.
|
* The parent to this chunk, if any.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Chunk parent;
|
private final Chunk parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size of the chunk header in bytes.
|
||||||
|
*/
|
||||||
|
protected final int headerSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* headerSize + dataSize. The total size of this chunk.
|
||||||
|
*/
|
||||||
|
protected final int chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offset of this chunk from the start of the file.
|
||||||
|
*/
|
||||||
|
protected final int offset;
|
||||||
|
|
||||||
protected Chunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected Chunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
offset = buffer.position() - 2;
|
offset = buffer.position() - 2;
|
||||||
@@ -72,74 +122,6 @@ public abstract class Chunk implements SerializableResource {
|
|||||||
chunkSize = buffer.getInt();
|
chunkSize = buffer.getInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new chunk whose contents start at {@code buffer}'s current position.
|
|
||||||
*
|
|
||||||
* @param buffer A buffer positioned at the start of a chunk.
|
|
||||||
* @return new chunk
|
|
||||||
*/
|
|
||||||
public static Chunk newInstance(ByteBuffer buffer) {
|
|
||||||
return newInstance(buffer, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new chunk whose contents start at {@code buffer}'s current position.
|
|
||||||
*
|
|
||||||
* @param buffer A buffer positioned at the start of a chunk.
|
|
||||||
* @param parent The parent to this chunk (or null if there's no parent).
|
|
||||||
* @return new chunk
|
|
||||||
*/
|
|
||||||
public static Chunk newInstance(ByteBuffer buffer, @Nullable Chunk parent) {
|
|
||||||
Chunk result;
|
|
||||||
Type type = Type.fromCode(buffer.getShort());
|
|
||||||
switch (type) {
|
|
||||||
case STRING_POOL:
|
|
||||||
result = new StringPoolChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case TABLE:
|
|
||||||
result = new ResourceTableChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML:
|
|
||||||
result = new XmlChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_START_NAMESPACE:
|
|
||||||
result = new XmlNamespaceStartChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_END_NAMESPACE:
|
|
||||||
result = new XmlNamespaceEndChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_START_ELEMENT:
|
|
||||||
result = new XmlStartElementChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_END_ELEMENT:
|
|
||||||
result = new XmlEndElementChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_CDATA:
|
|
||||||
result = new XmlCdataChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case XML_RESOURCE_MAP:
|
|
||||||
result = new XmlResourceMapChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case TABLE_PACKAGE:
|
|
||||||
result = new PackageChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case TABLE_TYPE:
|
|
||||||
result = new TypeChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case TABLE_TYPE_SPEC:
|
|
||||||
result = new TypeSpecChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
case TABLE_LIBRARY:
|
|
||||||
result = new LibraryChunk(buffer, parent);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
result = new UnknownChunk(buffer, parent);
|
|
||||||
}
|
|
||||||
result.init(buffer);
|
|
||||||
result.seekToEndOfChunk(buffer);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finishes initialization of a chunk. This should be called immediately after the constructor.
|
* Finishes initialization of a chunk. This should be called immediately after the constructor.
|
||||||
* This is separate from the constructor so that the header of a chunk can be fully initialized
|
* This is separate from the constructor so that the header of a chunk can be fully initialized
|
||||||
@@ -274,46 +256,70 @@ public abstract class Chunk implements SerializableResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of chunks that can exist.
|
* Creates a new chunk whose contents start at {@code buffer}'s current position.
|
||||||
|
*
|
||||||
|
* @param buffer A buffer positioned at the start of a chunk.
|
||||||
|
* @return new chunk
|
||||||
*/
|
*/
|
||||||
public enum Type {
|
public static Chunk newInstance(ByteBuffer buffer) {
|
||||||
NULL(0x0000),
|
return newInstance(buffer, null);
|
||||||
STRING_POOL(0x0001),
|
}
|
||||||
TABLE(0x0002),
|
|
||||||
XML(0x0003),
|
|
||||||
XML_START_NAMESPACE(0x0100),
|
|
||||||
XML_END_NAMESPACE(0x0101),
|
|
||||||
XML_START_ELEMENT(0x0102),
|
|
||||||
XML_END_ELEMENT(0x0103),
|
|
||||||
XML_CDATA(0x0104),
|
|
||||||
XML_RESOURCE_MAP(0x0180),
|
|
||||||
TABLE_PACKAGE(0x0200),
|
|
||||||
TABLE_TYPE(0x0201),
|
|
||||||
TABLE_TYPE_SPEC(0x0202),
|
|
||||||
TABLE_LIBRARY(0x0203);
|
|
||||||
|
|
||||||
private static final Map<Short, Type> FROM_SHORT;
|
/**
|
||||||
|
* Creates a new chunk whose contents start at {@code buffer}'s current position.
|
||||||
static {
|
*
|
||||||
Builder<Short, Type> builder = ImmutableMap.builder();
|
* @param buffer A buffer positioned at the start of a chunk.
|
||||||
for (Type type : values()) {
|
* @param parent The parent to this chunk (or null if there's no parent).
|
||||||
builder.put(type.code(), type);
|
* @return new chunk
|
||||||
}
|
*/
|
||||||
FROM_SHORT = builder.build();
|
public static Chunk newInstance(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
}
|
Chunk result;
|
||||||
|
Type type = Type.fromCode(buffer.getShort());
|
||||||
private final short code;
|
switch (type) {
|
||||||
|
case STRING_POOL:
|
||||||
Type(int code) {
|
result = new StringPoolChunk(buffer, parent);
|
||||||
this.code = Shorts.checkedCast(code);
|
break;
|
||||||
}
|
case TABLE:
|
||||||
|
result = new ResourceTableChunk(buffer, parent);
|
||||||
public static Type fromCode(short code) {
|
break;
|
||||||
return Preconditions.checkNotNull(FROM_SHORT.get(code), "Unknown chunk type: %s", code);
|
case XML:
|
||||||
}
|
result = new XmlChunk(buffer, parent);
|
||||||
|
break;
|
||||||
public short code() {
|
case XML_START_NAMESPACE:
|
||||||
return code;
|
result = new XmlNamespaceStartChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case XML_END_NAMESPACE:
|
||||||
|
result = new XmlNamespaceEndChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case XML_START_ELEMENT:
|
||||||
|
result = new XmlStartElementChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case XML_END_ELEMENT:
|
||||||
|
result = new XmlEndElementChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case XML_CDATA:
|
||||||
|
result = new XmlCdataChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case XML_RESOURCE_MAP:
|
||||||
|
result = new XmlResourceMapChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case TABLE_PACKAGE:
|
||||||
|
result = new PackageChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case TABLE_TYPE:
|
||||||
|
result = new TypeChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case TABLE_TYPE_SPEC:
|
||||||
|
result = new TypeSpecChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
case TABLE_LIBRARY:
|
||||||
|
result = new LibraryChunk(buffer, parent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = new UnknownChunk(buffer, parent);
|
||||||
}
|
}
|
||||||
|
result.init(buffer);
|
||||||
|
result.seekToEndOfChunk(buffer);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public final class LibraryChunk extends Chunk {
|
|||||||
*/
|
*/
|
||||||
private final List<Entry> entries = new ArrayList<>();
|
private final List<Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
LibraryChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected LibraryChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
entryCount = buffer.getInt();
|
entryCount = buffer.getInt();
|
||||||
}
|
}
|
||||||
@@ -98,17 +98,17 @@ public final class LibraryChunk extends Chunk {
|
|||||||
private final int packageId;
|
private final int packageId;
|
||||||
private final String packageName;
|
private final String packageName;
|
||||||
|
|
||||||
private Entry(int packageId, String packageName) {
|
|
||||||
this.packageId = packageId;
|
|
||||||
this.packageName = packageName;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Entry create(ByteBuffer buffer, int offset) {
|
static Entry create(ByteBuffer buffer, int offset) {
|
||||||
int packageId = buffer.getInt(offset);
|
int packageId = buffer.getInt(offset);
|
||||||
String packageName = PackageUtils.readPackageName(buffer, offset + 4);
|
String packageName = PackageUtils.readPackageName(buffer, offset + 4);
|
||||||
return new Entry(packageId, packageName);
|
return new Entry(packageId, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Entry(int packageId, String packageName) {
|
||||||
|
this.packageId = packageId;
|
||||||
|
this.packageName = packageName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The id assigned to the shared library at build time.
|
* The id assigned to the shared library at build time.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -43,48 +43,58 @@ public final class PackageChunk extends ChunkWithChunks {
|
|||||||
* Offset in bytes, from the start of the chunk, where {@code keyStringsOffset} can be found.
|
* Offset in bytes, from the start of the chunk, where {@code keyStringsOffset} can be found.
|
||||||
*/
|
*/
|
||||||
private static final int KEY_OFFSET_OFFSET = 276;
|
private static final int KEY_OFFSET_OFFSET = 276;
|
||||||
/**
|
|
||||||
* The offset (from {@code offset}) in the original buffer where type strings start.
|
|
||||||
*/
|
|
||||||
private final int typeStringsOffset;
|
|
||||||
/**
|
|
||||||
* The index into the type string pool of the last public type.
|
|
||||||
*/
|
|
||||||
private final int lastPublicType;
|
|
||||||
/**
|
|
||||||
* An offset to the string pool that contains the key strings for this package.
|
|
||||||
*/
|
|
||||||
private final int keyStringsOffset;
|
|
||||||
/**
|
|
||||||
* The index into the key string pool of the last public key.
|
|
||||||
*/
|
|
||||||
private final int lastPublicKey;
|
|
||||||
/**
|
|
||||||
* An offset to the type ID(s). This is undocumented in the original code.
|
|
||||||
*/
|
|
||||||
private final int typeIdOffset;
|
|
||||||
/**
|
|
||||||
* Contains a mapping of a type index to its {@link TypeSpecChunk}.
|
|
||||||
*/
|
|
||||||
private final Map<Integer, TypeSpecChunk> typeSpecs = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* Contains a mapping of a type index to all of the {@link TypeChunk} with that index.
|
|
||||||
*/
|
|
||||||
private final Multimap<Integer, TypeChunk> types = ArrayListMultimap.create();
|
|
||||||
/**
|
/**
|
||||||
* The package id if this is a base package, or 0 if not a base package.
|
* The package id if this is a base package, or 0 if not a base package.
|
||||||
*/
|
*/
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the package.
|
* The name of the package.
|
||||||
*/
|
*/
|
||||||
private String packageName;
|
private String packageName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset (from {@code offset}) in the original buffer where type strings start.
|
||||||
|
*/
|
||||||
|
private final int typeStringsOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index into the type string pool of the last public type.
|
||||||
|
*/
|
||||||
|
private final int lastPublicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An offset to the string pool that contains the key strings for this package.
|
||||||
|
*/
|
||||||
|
private final int keyStringsOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index into the key string pool of the last public key.
|
||||||
|
*/
|
||||||
|
private final int lastPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An offset to the type ID(s). This is undocumented in the original code.
|
||||||
|
*/
|
||||||
|
private final int typeIdOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a mapping of a type index to its {@link TypeSpecChunk}.
|
||||||
|
*/
|
||||||
|
private final Map<Integer, TypeSpecChunk> typeSpecs = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains a mapping of a type index to all of the {@link TypeChunk} with that index.
|
||||||
|
*/
|
||||||
|
private final Multimap<Integer, TypeChunk> types = ArrayListMultimap.create();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May contain a library chunk for mapping dynamic references to resolved references.
|
* May contain a library chunk for mapping dynamic references to resolved references.
|
||||||
*/
|
*/
|
||||||
private Optional<LibraryChunk> libraryChunk = Optional.empty();
|
private Optional<LibraryChunk> libraryChunk = Optional.empty();
|
||||||
|
|
||||||
PackageChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected PackageChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
id = buffer.getInt();
|
id = buffer.getInt();
|
||||||
packageName = PackageUtils.readPackageName(buffer, buffer.position());
|
packageName = PackageUtils.readPackageName(buffer, buffer.position());
|
||||||
|
|||||||
@@ -36,16 +36,17 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class ResourceTableChunk extends ChunkWithChunks {
|
public final class ResourceTableChunk extends ChunkWithChunks {
|
||||||
|
|
||||||
/**
|
|
||||||
* The packages contained in this resource table.
|
|
||||||
*/
|
|
||||||
private final Map<String, PackageChunk> packages = new HashMap<>();
|
|
||||||
/**
|
/**
|
||||||
* A string pool containing all string resource values in the entire resource table.
|
* A string pool containing all string resource values in the entire resource table.
|
||||||
*/
|
*/
|
||||||
private StringPoolChunk stringPool;
|
private StringPoolChunk stringPool;
|
||||||
|
|
||||||
ResourceTableChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
/**
|
||||||
|
* The packages contained in this resource table.
|
||||||
|
*/
|
||||||
|
private final Map<String, PackageChunk> packages = new HashMap<>();
|
||||||
|
|
||||||
|
protected ResourceTableChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
// packageCount. We ignore this, because we already know how many chunks we have.
|
// packageCount. We ignore this, because we already know how many chunks we have.
|
||||||
Preconditions.checkState(buffer.getInt() >= 1, "ResourceTableChunk package count was < 1.");
|
Preconditions.checkState(buffer.getInt() >= 1, "ResourceTableChunk package count was < 1.");
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public final class StringPoolChunk extends Chunk {
|
|||||||
*/
|
*/
|
||||||
private boolean isOriginalDeduped = false;
|
private boolean isOriginalDeduped = false;
|
||||||
|
|
||||||
StringPoolChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected StringPoolChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
stringCount = buffer.getInt();
|
stringCount = buffer.getInt();
|
||||||
styleCount = buffer.getInt();
|
styleCount = buffer.getInt();
|
||||||
@@ -310,10 +310,6 @@ public final class StringPoolChunk extends Chunk {
|
|||||||
static final int RES_STRING_POOL_SPAN_END = 0xFFFFFFFF;
|
static final int RES_STRING_POOL_SPAN_END = 0xFFFFFFFF;
|
||||||
private final List<StringPoolSpan> spans;
|
private final List<StringPoolSpan> spans;
|
||||||
|
|
||||||
private StringPoolStyle(List<StringPoolSpan> spans) {
|
|
||||||
this.spans = spans;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StringPoolStyle create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
|
static StringPoolStyle create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
|
||||||
Builder<StringPoolSpan> spans = ImmutableList.builder();
|
Builder<StringPoolSpan> spans = ImmutableList.builder();
|
||||||
int nameIndex = buffer.getInt(offset);
|
int nameIndex = buffer.getInt(offset);
|
||||||
@@ -325,6 +321,10 @@ public final class StringPoolChunk extends Chunk {
|
|||||||
return new StringPoolStyle(spans.build());
|
return new StringPoolStyle(spans.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StringPoolStyle(List<StringPoolSpan> spans) {
|
||||||
|
this.spans = spans;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] toByteArray() throws IOException {
|
public byte[] toByteArray() throws IOException {
|
||||||
return toByteArray(false);
|
return toByteArray(false);
|
||||||
@@ -384,13 +384,6 @@ public final class StringPoolChunk extends Chunk {
|
|||||||
private final int stop;
|
private final int stop;
|
||||||
private final StringPoolChunk parent;
|
private final StringPoolChunk parent;
|
||||||
|
|
||||||
private StringPoolSpan(int nameIndex, int start, int stop, StringPoolChunk parent) {
|
|
||||||
this.nameIndex = nameIndex;
|
|
||||||
this.start = start;
|
|
||||||
this.stop = stop;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StringPoolSpan create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
|
static StringPoolSpan create(ByteBuffer buffer, int offset, StringPoolChunk parent) {
|
||||||
int nameIndex = buffer.getInt(offset);
|
int nameIndex = buffer.getInt(offset);
|
||||||
int start = buffer.getInt(offset + 4);
|
int start = buffer.getInt(offset + 4);
|
||||||
@@ -398,6 +391,13 @@ public final class StringPoolChunk extends Chunk {
|
|||||||
return new StringPoolSpan(nameIndex, start, stop, parent);
|
return new StringPoolSpan(nameIndex, start, stop, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StringPoolSpan(int nameIndex, int start, int stop, StringPoolChunk parent) {
|
||||||
|
this.nameIndex = nameIndex;
|
||||||
|
this.start = start;
|
||||||
|
this.stop = stop;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final byte[] toByteArray() {
|
public final byte[] toByteArray() {
|
||||||
return toByteArray(false);
|
return toByteArray(false);
|
||||||
|
|||||||
@@ -52,16 +52,18 @@ public final class TypeChunk extends Chunk {
|
|||||||
* The offset (from {@code offset}) in the original buffer where {@code entries} start.
|
* The offset (from {@code offset}) in the original buffer where {@code entries} start.
|
||||||
*/
|
*/
|
||||||
private final int entriesStart;
|
private final int entriesStart;
|
||||||
/**
|
|
||||||
* A sparse list of resource entries defined by this chunk.
|
|
||||||
*/
|
|
||||||
private final Map<Integer, Entry> entries = new TreeMap<>();
|
|
||||||
/**
|
/**
|
||||||
* The resource configuration that these resource entries correspond to.
|
* The resource configuration that these resource entries correspond to.
|
||||||
*/
|
*/
|
||||||
private BinaryResourceConfiguration configuration;
|
private BinaryResourceConfiguration configuration;
|
||||||
|
|
||||||
TypeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
/**
|
||||||
|
* A sparse list of resource entries defined by this chunk.
|
||||||
|
*/
|
||||||
|
private final Map<Integer, Entry> entries = new TreeMap<>();
|
||||||
|
|
||||||
|
protected TypeChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
id = UnsignedBytes.toInt(buffer.get());
|
id = UnsignedBytes.toInt(buffer.get());
|
||||||
buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
|
buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
|
||||||
@@ -174,13 +176,13 @@ public final class TypeChunk extends Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getString(int index) {
|
protected String getString(int index) {
|
||||||
ResourceTableChunk resourceTable = getResourceTableChunk();
|
ResourceTableChunk resourceTable = getResourceTableChunk();
|
||||||
Preconditions.checkNotNull(resourceTable, "%s has no resource table.", getClass());
|
Preconditions.checkNotNull(resourceTable, "%s has no resource table.", getClass());
|
||||||
return resourceTable.getStringPool().getString(index);
|
return resourceTable.getStringPool().getString(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getKeyName(int index) {
|
protected String getKeyName(int index) {
|
||||||
PackageChunk packageChunk = getPackageChunk();
|
PackageChunk packageChunk = getPackageChunk();
|
||||||
Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
|
Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
|
||||||
StringPoolChunk keyPool = packageChunk.getKeyStringPool();
|
StringPoolChunk keyPool = packageChunk.getKeyStringPool();
|
||||||
@@ -188,7 +190,7 @@ public final class TypeChunk extends Chunk {
|
|||||||
return keyPool.getString(index);
|
return keyPool.getString(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateKey(int index, String value) {
|
protected void updateKey(int index, String value) {
|
||||||
PackageChunk packageChunk = getPackageChunk();
|
PackageChunk packageChunk = getPackageChunk();
|
||||||
Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
|
Preconditions.checkNotNull(packageChunk, "%s has no parent package.", getClass());
|
||||||
StringPoolChunk keyPool = packageChunk.getKeyStringPool();
|
StringPoolChunk keyPool = packageChunk.getKeyStringPool();
|
||||||
@@ -291,10 +293,10 @@ public final class TypeChunk extends Chunk {
|
|||||||
private final int headerSize;
|
private final int headerSize;
|
||||||
private final int flags;
|
private final int flags;
|
||||||
private final int keyIndex;
|
private final int keyIndex;
|
||||||
|
private BinaryResourceValue value;
|
||||||
|
private Map<Integer, BinaryResourceValue> values;
|
||||||
private final int parentEntry;
|
private final int parentEntry;
|
||||||
private final TypeChunk parent;
|
private final TypeChunk parent;
|
||||||
private BinaryResourceValue value;
|
|
||||||
private final Map<Integer, BinaryResourceValue> values;
|
|
||||||
|
|
||||||
private Entry(int headerSize,
|
private Entry(int headerSize,
|
||||||
int flags,
|
int flags,
|
||||||
@@ -312,52 +314,6 @@ public final class TypeChunk extends Chunk {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link Entry} whose contents start at the 0-based position in
|
|
||||||
* {@code buffer} given by a 4-byte value read from {@code buffer} and then added to
|
|
||||||
* {@code baseOffset}. If the value read from {@code buffer} is equal to {@link #NO_ENTRY}, then
|
|
||||||
* null is returned as there is no resource at that position.
|
|
||||||
*
|
|
||||||
* <p>Otherwise, this position is parsed and returned as an {@link Entry}.
|
|
||||||
*
|
|
||||||
* @param buffer A buffer positioned at an offset to an {@link Entry}.
|
|
||||||
* @param baseOffset Offset that must be added to the value at {@code buffer}'s position.
|
|
||||||
* @param parent The {@link TypeChunk} that this resource entry belongs to.
|
|
||||||
* @return New {@link Entry} or null if there is no resource at this location.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static Entry create(ByteBuffer buffer, int baseOffset, TypeChunk parent) {
|
|
||||||
int offset = buffer.getInt();
|
|
||||||
if (offset == NO_ENTRY) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int position = buffer.position();
|
|
||||||
buffer.position(baseOffset + offset); // Set buffer position to resource entry start
|
|
||||||
Entry result = newInstance(buffer, parent);
|
|
||||||
buffer.position(position); // Restore buffer position
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Entry newInstance(ByteBuffer buffer, TypeChunk parent) {
|
|
||||||
int headerSize = buffer.getShort() & 0xFFFF;
|
|
||||||
int flags = buffer.getShort() & 0xFFFF;
|
|
||||||
int keyIndex = buffer.getInt();
|
|
||||||
BinaryResourceValue value = null;
|
|
||||||
Map<Integer, BinaryResourceValue> values = new LinkedHashMap<>();
|
|
||||||
int parentEntry = 0;
|
|
||||||
if ((flags & FLAG_COMPLEX) != 0) {
|
|
||||||
parentEntry = buffer.getInt();
|
|
||||||
int valueCount = buffer.getInt();
|
|
||||||
for (int i = 0; i < valueCount; ++i) {
|
|
||||||
values.put(buffer.getInt(), BinaryResourceValue.create(buffer));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = BinaryResourceValue.create(buffer);
|
|
||||||
}
|
|
||||||
return new Entry(headerSize, flags, keyIndex, value, values, parentEntry, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of bytes in the header of the {@link Entry}.
|
* Number of bytes in the header of the {@link Entry}.
|
||||||
*/
|
*/
|
||||||
@@ -466,6 +422,52 @@ public final class TypeChunk extends Chunk {
|
|||||||
return (flags() & FLAG_COMPLEX) != 0;
|
return (flags() & FLAG_COMPLEX) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Entry} whose contents start at the 0-based position in
|
||||||
|
* {@code buffer} given by a 4-byte value read from {@code buffer} and then added to
|
||||||
|
* {@code baseOffset}. If the value read from {@code buffer} is equal to {@link #NO_ENTRY}, then
|
||||||
|
* null is returned as there is no resource at that position.
|
||||||
|
*
|
||||||
|
* <p>Otherwise, this position is parsed and returned as an {@link Entry}.
|
||||||
|
*
|
||||||
|
* @param buffer A buffer positioned at an offset to an {@link Entry}.
|
||||||
|
* @param baseOffset Offset that must be added to the value at {@code buffer}'s position.
|
||||||
|
* @param parent The {@link TypeChunk} that this resource entry belongs to.
|
||||||
|
* @return New {@link Entry} or null if there is no resource at this location.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Entry create(ByteBuffer buffer, int baseOffset, TypeChunk parent) {
|
||||||
|
int offset = buffer.getInt();
|
||||||
|
if (offset == NO_ENTRY) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int position = buffer.position();
|
||||||
|
buffer.position(baseOffset + offset); // Set buffer position to resource entry start
|
||||||
|
Entry result = newInstance(buffer, parent);
|
||||||
|
buffer.position(position); // Restore buffer position
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Entry newInstance(ByteBuffer buffer, TypeChunk parent) {
|
||||||
|
int headerSize = buffer.getShort() & 0xFFFF;
|
||||||
|
int flags = buffer.getShort() & 0xFFFF;
|
||||||
|
int keyIndex = buffer.getInt();
|
||||||
|
BinaryResourceValue value = null;
|
||||||
|
Map<Integer, BinaryResourceValue> values = new LinkedHashMap<>();
|
||||||
|
int parentEntry = 0;
|
||||||
|
if ((flags & FLAG_COMPLEX) != 0) {
|
||||||
|
parentEntry = buffer.getInt();
|
||||||
|
int valueCount = buffer.getInt();
|
||||||
|
for (int i = 0; i < valueCount; ++i) {
|
||||||
|
values.put(buffer.getInt(), BinaryResourceValue.create(buffer));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = BinaryResourceValue.create(buffer);
|
||||||
|
}
|
||||||
|
return new Entry(headerSize, flags, keyIndex, value, values, parentEntry, parent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final byte[] toByteArray() {
|
public final byte[] toByteArray() {
|
||||||
return toByteArray(false);
|
return toByteArray(false);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public final class TypeSpecChunk extends Chunk {
|
|||||||
*/
|
*/
|
||||||
private final int[] resources;
|
private final int[] resources;
|
||||||
|
|
||||||
TypeSpecChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected TypeSpecChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
id = UnsignedBytes.toInt(buffer.get());
|
id = UnsignedBytes.toInt(buffer.get());
|
||||||
buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
|
buffer.position(buffer.position() + 3); // Skip 3 bytes for packing
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public final class UnknownChunk extends Chunk {
|
|||||||
|
|
||||||
private final byte[] payload;
|
private final byte[] payload;
|
||||||
|
|
||||||
UnknownChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected UnknownChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
|
|
||||||
type = Type.fromCode(buffer.getShort(offset));
|
type = Type.fromCode(buffer.getShort(offset));
|
||||||
|
|||||||
@@ -36,18 +36,6 @@ public class XmlAttribute implements SerializableResource {
|
|||||||
private final BinaryResourceValue typedValue;
|
private final BinaryResourceValue typedValue;
|
||||||
private final XmlNodeChunk parent;
|
private final XmlNodeChunk parent;
|
||||||
|
|
||||||
private XmlAttribute(int namespaceIndex,
|
|
||||||
int nameIndex,
|
|
||||||
int rawValueIndex,
|
|
||||||
BinaryResourceValue typedValue,
|
|
||||||
XmlNodeChunk parent) {
|
|
||||||
this.namespaceIndex = namespaceIndex;
|
|
||||||
this.nameIndex = nameIndex;
|
|
||||||
this.rawValueIndex = rawValueIndex;
|
|
||||||
this.typedValue = typedValue;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link XmlAttribute} based on the bytes at the current {@code buffer} position.
|
* Creates a new {@link XmlAttribute} based on the bytes at the current {@code buffer} position.
|
||||||
*
|
*
|
||||||
@@ -62,6 +50,18 @@ public class XmlAttribute implements SerializableResource {
|
|||||||
return new XmlAttribute(namespace, name, rawValue, typedValue, parent);
|
return new XmlAttribute(namespace, name, rawValue, typedValue, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private XmlAttribute(int namespaceIndex,
|
||||||
|
int nameIndex,
|
||||||
|
int rawValueIndex,
|
||||||
|
BinaryResourceValue typedValue,
|
||||||
|
XmlNodeChunk parent) {
|
||||||
|
this.namespaceIndex = namespaceIndex;
|
||||||
|
this.nameIndex = nameIndex;
|
||||||
|
this.rawValueIndex = rawValueIndex;
|
||||||
|
this.typedValue = typedValue;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string reference to the namespace URI, or -1 if not present.
|
* A string reference to the namespace URI, or -1 if not present.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public final class XmlCdataChunk extends XmlNodeChunk {
|
|||||||
*/
|
*/
|
||||||
private final BinaryResourceValue binaryResourceValue;
|
private final BinaryResourceValue binaryResourceValue;
|
||||||
|
|
||||||
XmlCdataChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlCdataChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
rawValue = buffer.getInt();
|
rawValue = buffer.getInt();
|
||||||
binaryResourceValue = BinaryResourceValue.create(buffer);
|
binaryResourceValue = BinaryResourceValue.create(buffer);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.nio.ByteBuffer;
|
|||||||
*/
|
*/
|
||||||
public final class XmlChunk extends ChunkWithChunks {
|
public final class XmlChunk extends ChunkWithChunks {
|
||||||
|
|
||||||
XmlChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public final class XmlEndElementChunk extends XmlNodeChunk {
|
|||||||
*/
|
*/
|
||||||
private final int name;
|
private final int name;
|
||||||
|
|
||||||
XmlEndElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlEndElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
namespace = buffer.getInt();
|
namespace = buffer.getInt();
|
||||||
name = buffer.getInt();
|
name = buffer.getInt();
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ import java.nio.ByteBuffer;
|
|||||||
*/
|
*/
|
||||||
public final class XmlNamespaceEndChunk extends XmlNamespaceChunk {
|
public final class XmlNamespaceEndChunk extends XmlNamespaceChunk {
|
||||||
|
|
||||||
XmlNamespaceEndChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlNamespaceEndChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ import java.nio.ByteBuffer;
|
|||||||
*/
|
*/
|
||||||
public final class XmlNamespaceStartChunk extends XmlNamespaceChunk {
|
public final class XmlNamespaceStartChunk extends XmlNamespaceChunk {
|
||||||
|
|
||||||
XmlNamespaceStartChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlNamespaceStartChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -71,7 +71,7 @@ public final class XmlStartElementChunk extends XmlNodeChunk {
|
|||||||
*/
|
*/
|
||||||
private final List<XmlAttribute> attributes = new ArrayList<>();
|
private final List<XmlAttribute> attributes = new ArrayList<>();
|
||||||
|
|
||||||
XmlStartElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
protected XmlStartElementChunk(ByteBuffer buffer, @Nullable Chunk parent) {
|
||||||
super(buffer, parent);
|
super(buffer, parent);
|
||||||
namespace = buffer.getInt();
|
namespace = buffer.getInt();
|
||||||
name = buffer.getInt();
|
name = buffer.getInt();
|
||||||
|
|||||||
@@ -0,0 +1,342 @@
|
|||||||
|
package com.guardanis.applock;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
|
import androidx.core.os.CancellationSignal;
|
||||||
|
|
||||||
|
import com.vtosters.lite.R;
|
||||||
|
|
||||||
|
import com.guardanis.applock.activities.UnlockActivity;
|
||||||
|
import com.guardanis.applock.services.FingerprintLockService;
|
||||||
|
import com.guardanis.applock.services.FingerprintLockService.AuthenticationDelegate;
|
||||||
|
import com.guardanis.applock.services.LockService;
|
||||||
|
import com.guardanis.applock.services.PINLockService;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class AppLock {
|
||||||
|
|
||||||
|
public static final int REQUEST_CODE_UNLOCK = 9371;
|
||||||
|
public static final int REQUEST_CODE_LOCK_CREATION = 9372;
|
||||||
|
public static final int REQUEST_CODE_FINGERPRINT_PERMISSION = 9373;
|
||||||
|
public static final int ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE = 1;
|
||||||
|
public static final int ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED = 2;
|
||||||
|
public static final int ERROR_CODE_FINGERPRINTS_EMPTY = 3;
|
||||||
|
public static final int ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED = 4;
|
||||||
|
public static final int ERROR_CODE_SDK_VERSION_MINIMUM = 5;
|
||||||
|
private static final String PREFS = "pin__preferences";
|
||||||
|
private static final String PREF_UNLOCK_FAILURE_TIME = "pin__unlock_failure_time";
|
||||||
|
private static final String PREF_UNLOCK_SUCCESS_TIME = "pin__unlock_success_time";
|
||||||
|
private static AppLock instance;
|
||||||
|
protected Context context;
|
||||||
|
protected HashMap<Class<? extends LockService>, LockService> lockServices = new HashMap<>();
|
||||||
|
protected int unlockAttemptsCount = 1;
|
||||||
|
|
||||||
|
protected AppLock(Context context) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
|
||||||
|
this.lockServices.put(PINLockService.class, new PINLockService());
|
||||||
|
this.lockServices.put(FingerprintLockService.class, new FingerprintLockService());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppLock getInstance(Context context) {
|
||||||
|
if (instance == null)
|
||||||
|
instance = new AppLock(context);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the user has enrolled in either PIN or Fingerprint locking
|
||||||
|
*/
|
||||||
|
public static boolean isEnrolled(Context context) {
|
||||||
|
AppLock helper = getInstance(context);
|
||||||
|
|
||||||
|
for (LockService service : helper.lockServices.values()) {
|
||||||
|
if (service.isEnrolled(context))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setAuthenticationRequired(Context context) {
|
||||||
|
AppLock helper = getInstance(context);
|
||||||
|
|
||||||
|
helper.getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the user is enrolled in locking and the last successful unlock happened more than the default lock duration ago
|
||||||
|
*/
|
||||||
|
public static boolean isUnlockRequired(Context context) {
|
||||||
|
int minutes = Integer.parseInt(getPinCodeTime(context));
|
||||||
|
|
||||||
|
if (minutes < 1) {
|
||||||
|
minutes = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isUnlockRequired(context, TimeUnit.MINUTES.toMillis(minutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPinCodeTime(Context context) {
|
||||||
|
return context.getString(AndroidUtils.getIdentifier("applock__activity_lock_reenable_minutes", "integer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPinCodeLimits(Context context) {
|
||||||
|
return context.getString(AndroidUtils.getIdentifier("applock__input_pin_item_count", "integer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the user is enrolled in locking and the last successful unlock happened more than lastSuccessValidMs ago
|
||||||
|
*/
|
||||||
|
public static boolean isUnlockRequired(Context context, long lastSuccessValidMs) {
|
||||||
|
return isEnrolled(context) && lastSuccessValidMs < System.currentTimeMillis() - getUnlockSuccessTime(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static long getUnlockSuccessTime(Context context) {
|
||||||
|
return AppLock.getInstance(context)
|
||||||
|
.getPreferences()
|
||||||
|
.getLong(PREF_UNLOCK_SUCCESS_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onActivityResumed(Activity activity) {
|
||||||
|
if (isUnlockRequired(activity)) {
|
||||||
|
boolean unlockActivityReturnAllowed = activity.getResources()
|
||||||
|
.getBoolean(AndroidUtils.getIdentifier("applock__unlock_activity_return_allowed", "bool"));
|
||||||
|
|
||||||
|
Intent intent = new Intent(activity, UnlockActivity.class)
|
||||||
|
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, unlockActivityReturnAllowed);
|
||||||
|
|
||||||
|
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an action-based unlock is required and navigates to the UnlockActivity if true.
|
||||||
|
*
|
||||||
|
* @return true if unlock is required.
|
||||||
|
*/
|
||||||
|
public static boolean unlockIfRequired(Activity activity) {
|
||||||
|
if (isUnlockRequired(activity)) {
|
||||||
|
Intent intent = new Intent(activity, UnlockActivity.class)
|
||||||
|
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, true);
|
||||||
|
|
||||||
|
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attemptFingerprintUnlock(final UnlockDelegate eventListener) {
|
||||||
|
if (handleFailureBlocking(eventListener))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AuthenticationDelegate delegate = new AuthenticationDelegate() {
|
||||||
|
@Override
|
||||||
|
public void onResolutionRequired(int errorCode) {
|
||||||
|
// eventListener.onResolutionRequired(errorCode);
|
||||||
|
eventListener.requestOpenKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int code, CharSequence message) {
|
||||||
|
eventListener.onAuthenticationHelp(code, String.valueOf(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticating(CancellationSignal cancellationSignal) {
|
||||||
|
// Handled internally
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(@NonNull FingerprintManagerCompat.AuthenticationResult result) {
|
||||||
|
onUnlockSuccessful(eventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed(String message) {
|
||||||
|
Log.w(getClass().getSimpleName(), "Fingerprint authentication failed: " + message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getLockService(FingerprintLockService.class)
|
||||||
|
.authenticate(context, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attemptPINUnlock(String pin, final UnlockDelegate eventListener) {
|
||||||
|
if (handleFailureBlocking(eventListener))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PINLockService.AuthenticationDelegate delegate = new PINLockService.AuthenticationDelegate() {
|
||||||
|
@Override
|
||||||
|
public void onNoPIN() {
|
||||||
|
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_no_matching_pin_found", "string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPINDoesNotMatch() {
|
||||||
|
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_match_failed", "string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPINMatches() {
|
||||||
|
onUnlockSuccessful(eventListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onUnlockFailed(String message) {
|
||||||
|
handleUnlockFailure(message, eventListener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getLockService(PINLockService.class)
|
||||||
|
.authenticate(context, pin, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if failure blocking is enabled
|
||||||
|
*/
|
||||||
|
private boolean handleFailureBlocking(final UnlockDelegate eventListener) {
|
||||||
|
if (isUnlockFailureBlockEnabled()) {
|
||||||
|
unlockAttemptsCount++;
|
||||||
|
|
||||||
|
if (getFailureDelayMs() < System.currentTimeMillis() - getUnlockFailureBlockStart())
|
||||||
|
resetUnlockFailure();
|
||||||
|
else {
|
||||||
|
String message = String.format(
|
||||||
|
context.getString(AndroidUtils.getIdentifier("applock__unlock_error_retry_limit_exceeded", "string")),
|
||||||
|
formatTimeRemaining());
|
||||||
|
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onFailureLimitExceeded(message);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleUnlockFailure(String message, UnlockDelegate eventListener) {
|
||||||
|
this.unlockAttemptsCount++;
|
||||||
|
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onFailureLimitExceeded(message);
|
||||||
|
|
||||||
|
if (context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount)
|
||||||
|
onFailureExceedsLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getPreferences() {
|
||||||
|
return context.getSharedPreferences(PREFS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onFailureExceedsLimit() {
|
||||||
|
getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putLong(PREF_UNLOCK_FAILURE_TIME, System.currentTimeMillis())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnlockFailureBlockEnabled() {
|
||||||
|
return context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount
|
||||||
|
|| System.currentTimeMillis() - getUnlockFailureBlockStart() < getFailureDelayMs();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getUnlockFailureBlockStart() {
|
||||||
|
return getPreferences()
|
||||||
|
.getLong(PREF_UNLOCK_FAILURE_TIME, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onUnlockSuccessful(UnlockDelegate eventListener) {
|
||||||
|
getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putLong(PREF_UNLOCK_SUCCESS_TIME, System.currentTimeMillis())
|
||||||
|
.commit();
|
||||||
|
|
||||||
|
resetUnlockFailure();
|
||||||
|
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onUnlockSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the last login time to require the user to re-authenticate with
|
||||||
|
* AppLock the next time they are eligible to unlock.
|
||||||
|
*/
|
||||||
|
public void setAuthenticationRequired() {
|
||||||
|
getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resetUnlockFailure() {
|
||||||
|
unlockAttemptsCount = 1;
|
||||||
|
|
||||||
|
getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putLong(PREF_UNLOCK_FAILURE_TIME, 0)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String formatTimeRemaining() {
|
||||||
|
long millis = getFailureDelayMs() - (System.currentTimeMillis() - getUnlockFailureBlockStart());
|
||||||
|
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));
|
||||||
|
|
||||||
|
if (TimeUnit.MILLISECONDS.toMinutes(millis) < 1)
|
||||||
|
return String.format(AndroidUtils.getString(R.string.n_seconds), seconds);
|
||||||
|
else
|
||||||
|
return String.format(AndroidUtils.getString(R.string.n_mins_secs), TimeUnit.MILLISECONDS.toMinutes(millis), seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getFailureDelayMs() {
|
||||||
|
return TimeUnit.MINUTES.toMillis(context.getResources()
|
||||||
|
.getInteger(AndroidUtils.getIdentifier("applock__failure_retry_delay", "integer")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will remove all PIN and/or Fingerprint enrollment data.
|
||||||
|
* Users will need to re-enroll in AppLock after this call.
|
||||||
|
*/
|
||||||
|
public void invalidateEnrollments() {
|
||||||
|
resetUnlockFailure();
|
||||||
|
setAuthenticationRequired();
|
||||||
|
|
||||||
|
for (LockService service : lockServices.values())
|
||||||
|
service.invalidateEnrollments(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelPendingAuthentications() {
|
||||||
|
for (LockService service : lockServices.values())
|
||||||
|
service.cancelPendingAuthentications(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends LockService> T getLockService(Class<T> named) {
|
||||||
|
return (T) lockServices.get(named);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface UnlockDelegate {
|
||||||
|
void onUnlockSuccessful();
|
||||||
|
|
||||||
|
void onResolutionRequired(int errorCode);
|
||||||
|
|
||||||
|
void onAuthenticationHelp(int code, String message);
|
||||||
|
|
||||||
|
void onFailureLimitExceeded(String message);
|
||||||
|
void requestOpenKeyboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.guardanis.applock.activities;
|
||||||
|
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
|
||||||
|
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsetsController;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.guardanis.applock.views.LockCreationViewController;
|
||||||
|
|
||||||
|
|
||||||
|
public class LockCreationActivity extends AppCompatActivity implements LockCreationViewController.Delegate {
|
||||||
|
|
||||||
|
protected LockCreationViewController viewController;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstance) {
|
||||||
|
super.onCreate(savedInstance);
|
||||||
|
|
||||||
|
setContentView(getIdentifier("applock__activity_lock_creation", "layout"));
|
||||||
|
findViewById(getIdentifier("applock__activity_lock_creation", "id")).setBackgroundColor(getBackgroundContent());
|
||||||
|
((TextView) findViewById(getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
|
||||||
|
|
||||||
|
this.viewController = new LockCreationViewController(this, findViewById(getIdentifier("pin__container", "id")));
|
||||||
|
this.viewController.setDelegate(this);
|
||||||
|
this.viewController.setupRootFlow();
|
||||||
|
|
||||||
|
this.getWindow().setStatusBarColor(getBackgroundContent());
|
||||||
|
this.getWindow().setNavigationBarColor(getBackgroundContent());
|
||||||
|
|
||||||
|
if (!isDarkTheme()) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
|
||||||
|
} else {
|
||||||
|
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLockCreated() {
|
||||||
|
setResult(Activity.RESULT_OK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||||
|
super.onPointerCaptureChanged(hasCapture);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.guardanis.applock.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
|
||||||
|
public class LockableAppCompatActivity extends AppCompatActivity {
|
||||||
|
@Override
|
||||||
|
protected void onPostResume() {
|
||||||
|
super.onPostResume();
|
||||||
|
|
||||||
|
AppLock.onActivityResumed(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (requestCode == AppLock.REQUEST_CODE_UNLOCK && resultCode == Activity.RESULT_CANCELED)
|
||||||
|
handleUnlockActivityReturnFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleUnlockActivityReturnFailure() {
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.guardanis.applock.activities;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsetsController;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.guardanis.applock.views.UnlockViewController;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
||||||
|
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
|
||||||
|
|
||||||
|
public class UnlockActivity extends AppCompatActivity implements UnlockViewController.Delegate {
|
||||||
|
|
||||||
|
public static final String INTENT_ALLOW_UNLOCKED_EXIT = "pin__allow_activity_exit"; // false by default
|
||||||
|
|
||||||
|
protected UnlockViewController viewController;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstance) {
|
||||||
|
super.onCreate(savedInstance);
|
||||||
|
|
||||||
|
setContentView(AndroidUtils.getIdentifier("applock__activity_unlock", "layout"));
|
||||||
|
findViewById(AndroidUtils.getIdentifier("applock__activity_unlock", "id")).setBackgroundColor(getBackgroundContent());
|
||||||
|
((TextView) findViewById(AndroidUtils.getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
|
||||||
|
|
||||||
|
this.viewController = new UnlockViewController(this, findViewById(AndroidUtils.getIdentifier("pin__container", "id")));
|
||||||
|
this.viewController.setAutoAuthorizationEnabled(false); // Disable auto authorization so fingerprint doesn't crash onResume
|
||||||
|
this.viewController.setDelegate(this);
|
||||||
|
this.viewController.setupRootFlow();
|
||||||
|
this.viewController.setAutoAuthorizationEnabled(true);
|
||||||
|
|
||||||
|
this.getWindow().setStatusBarColor(getBackgroundContent());
|
||||||
|
this.getWindow().setNavigationBarColor(getBackgroundContent());
|
||||||
|
|
||||||
|
if (!isDarkTheme()) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
|
||||||
|
} else {
|
||||||
|
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnlockSuccessful() {
|
||||||
|
setResult(Activity.RESULT_OK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
if (event.getRepeatCount() == 0) handleBackPressed();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
handleBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleBackPressed() {
|
||||||
|
if (!getIntent().getBooleanExtra(INTENT_ALLOW_UNLOCKED_EXIT, false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||||
|
super.onPointerCaptureChanged(hasCapture);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.guardanis.applock.dialogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.app.AppCompatDialog;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.views.AppLockViewController;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public abstract class AppLockDialogBuilder<ALVC extends AppLockViewController> {
|
||||||
|
|
||||||
|
protected WeakReference<Activity> activity;
|
||||||
|
protected ALVC viewController;
|
||||||
|
|
||||||
|
protected WeakReference<AppCompatDialog> dialog = new WeakReference<AppCompatDialog>(null);
|
||||||
|
protected int layoutResId;
|
||||||
|
|
||||||
|
public AppLockDialogBuilder(Activity activity, int layoutResId) {
|
||||||
|
this.activity = new WeakReference<>(activity);
|
||||||
|
this.layoutResId = layoutResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dialog show() {
|
||||||
|
if (viewController != null)
|
||||||
|
throw new RuntimeException("You can only call show() once per AppLockDialogBuilder instance.");
|
||||||
|
|
||||||
|
final Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
View parent = activity.getLayoutInflater()
|
||||||
|
.inflate(layoutResId, null, false);
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
|
builder.setView(parent);
|
||||||
|
builder.setOnCancelListener(dialogInterface -> handleCanceled());
|
||||||
|
builder.setOnDismissListener(dialogInterface -> handleDismissed());
|
||||||
|
|
||||||
|
this.viewController = buildViewControllerInstance(parent);
|
||||||
|
this.viewController.setupRootFlow();
|
||||||
|
|
||||||
|
AppCompatDialog dialog = builder.show();
|
||||||
|
|
||||||
|
this.dialog = new WeakReference<>(dialog);
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ALVC buildViewControllerInstance(View parent);
|
||||||
|
|
||||||
|
protected void handleCanceled() {
|
||||||
|
dismissDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleDismissed() {
|
||||||
|
this.dialog = new WeakReference<>(null);
|
||||||
|
this.viewController.unregisterReceivers();
|
||||||
|
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.cancelPendingAuthentications();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dismissDialog() {
|
||||||
|
try {
|
||||||
|
AppCompatDialog dialog = this.dialog.get();
|
||||||
|
|
||||||
|
if (dialog == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Attempt to prevent stupid fucking uncatchable system error "View not Attached to Window" when dialog dismissed
|
||||||
|
Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();
|
||||||
|
if (context instanceof Activity) {
|
||||||
|
if (!((Activity) context).isFinishing()) {
|
||||||
|
if (!((Activity) context).isDestroyed())
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
dialog.dismiss();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e("AppLockDialogBuilder", "View not Attached to Window???", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialog = new WeakReference<>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ALVC getViewController() {
|
||||||
|
return viewController;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.guardanis.applock.dialogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.guardanis.applock.views.LockCreationViewController;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class LockCreationDialogBuilder extends AppLockDialogBuilder<LockCreationViewController> implements LockCreationViewController.Delegate {
|
||||||
|
|
||||||
|
protected Runnable lockCreatedCallback;
|
||||||
|
protected Runnable canceledCallback;
|
||||||
|
|
||||||
|
public LockCreationDialogBuilder(Activity activity) {
|
||||||
|
super(activity, AndroidUtils.getIdentifier("applock__lock_creation", "layout"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Runnable to be triggered when a user has successfully enrolled in locking.
|
||||||
|
*/
|
||||||
|
public LockCreationDialogBuilder onLockCreated(Runnable lockCreatedCallback) {
|
||||||
|
this.lockCreatedCallback = lockCreatedCallback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Runnable to be triggered when a user has canceled enrollment.
|
||||||
|
*/
|
||||||
|
public LockCreationDialogBuilder onCanceled(Runnable canceledCallback) {
|
||||||
|
this.canceledCallback = canceledCallback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LockCreationViewController buildViewControllerInstance(View parent) {
|
||||||
|
LockCreationViewController controller = new LockCreationViewController(activity.get(), parent);
|
||||||
|
controller.setDelegate(this);
|
||||||
|
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLockCreated() {
|
||||||
|
dismissDialog();
|
||||||
|
|
||||||
|
if (lockCreatedCallback != null)
|
||||||
|
lockCreatedCallback.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleCanceled() {
|
||||||
|
super.handleCanceled();
|
||||||
|
|
||||||
|
if (canceledCallback != null)
|
||||||
|
canceledCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.guardanis.applock.dialogs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.views.UnlockViewController;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class UnlockDialogBuilder extends AppLockDialogBuilder<UnlockViewController> implements UnlockViewController.Delegate {
|
||||||
|
|
||||||
|
protected Runnable unlockCallback;
|
||||||
|
protected Runnable canceledCallback;
|
||||||
|
|
||||||
|
public UnlockDialogBuilder(Activity activity) {
|
||||||
|
super(activity, AndroidUtils.getIdentifier("applock__unlock", "layout"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Runnable to be triggered when a user has successfully unlocked.
|
||||||
|
*/
|
||||||
|
public UnlockDialogBuilder onUnlocked(Runnable unlockCallback) {
|
||||||
|
this.unlockCallback = unlockCallback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Runnable to be triggered when a user has canceled unlocking.
|
||||||
|
*/
|
||||||
|
public UnlockDialogBuilder onCanceled(Runnable canceledCallback) {
|
||||||
|
this.canceledCallback = canceledCallback;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UnlockViewController buildViewControllerInstance(View parent) {
|
||||||
|
UnlockViewController controller = new UnlockViewController(activity.get(), parent);
|
||||||
|
controller.setDelegate(this);
|
||||||
|
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnlockSuccessful() {
|
||||||
|
dismissDialog();
|
||||||
|
triggerUnlockCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleCanceled() {
|
||||||
|
super.handleCanceled();
|
||||||
|
|
||||||
|
if (canceledCallback != null)
|
||||||
|
canceledCallback.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void triggerUnlockCallback() {
|
||||||
|
if (unlockCallback != null)
|
||||||
|
unlockCallback.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user is actually enrolled in locking before opening the unlock dialog.
|
||||||
|
* If the user is enrolled, show() will be called. Otherwise, this function will trigger this
|
||||||
|
* instance's UnlockDialogBuilder.unlockCallback immediately.
|
||||||
|
*
|
||||||
|
* @return Dialog if show() is called
|
||||||
|
*/
|
||||||
|
public Dialog showIfEnrolledOrSuccess() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!AppLock.isEnrolled(activity)) {
|
||||||
|
triggerUnlockCallback();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user is both enrolled in locking and that unlocking is required before
|
||||||
|
* opening the unlock dialog. If the user is enrolled, show() will be called. Otherwise,
|
||||||
|
* this function will trigger this instance's UnlockDialogBuilder.unlockCallback immediately.
|
||||||
|
*
|
||||||
|
* @param longValidDurationMs the milliseconds a user's last unlock is valid for
|
||||||
|
* @return Dialog if show() is called
|
||||||
|
*/
|
||||||
|
public Dialog showIfRequiredOrSuccess(long longValidDurationMs) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!AppLock.isUnlockRequired(activity, longValidDurationMs)) {
|
||||||
|
triggerUnlockCallback();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package com.guardanis.applock.pin;
|
||||||
|
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
interface InputTextListener {
|
||||||
|
void onTextEntered(int count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PINInputController implements TextView.OnEditorActionListener, InputTextListener {
|
||||||
|
|
||||||
|
private final WeakReference<PINInputView> inputView;
|
||||||
|
private WeakReference<InputEventListener> eventListener = new WeakReference<>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you want to actually receive events after this, make sure you call setInputEventListener
|
||||||
|
*/
|
||||||
|
public PINInputController(PINInputView inputView) {
|
||||||
|
this.inputView = new WeakReference<>(inputView);
|
||||||
|
|
||||||
|
inputView.setOnEditorActionListener(this);
|
||||||
|
inputView.setInputTextListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputController ensureKeyboardVisible() {
|
||||||
|
final PINInputView inputView = this.inputView.get();
|
||||||
|
|
||||||
|
if (inputView == null)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
inputView.postDelayed(inputView::ensureKeyboardVisible, 300);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputController setPasswordCharactersEnabled(boolean passwordCharacterEnabled) {
|
||||||
|
this.inputView.get().setPasswordCharactersEnabled(passwordCharacterEnabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputController setInputNumbersCount(int inputNumbersCount) {
|
||||||
|
this.inputView.get().setInputViewsCount(inputNumbersCount);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputController setInputEventListener(InputEventListener eventListener) {
|
||||||
|
this.eventListener = new WeakReference<>(eventListener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
||||||
|
PINInputView inputView = this.inputView.get();
|
||||||
|
|
||||||
|
if (inputView == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (isSoftKeyboardFinishedAction(textView, i, keyEvent)) {
|
||||||
|
InputEventListener eventListener = this.eventListener.get();
|
||||||
|
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onInputEntered(inputView.getText());
|
||||||
|
|
||||||
|
inputView.reset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSoftKeyboardFinishedAction(TextView view, int action, KeyEvent event) {
|
||||||
|
// Some devices return null event on editor actions for Enter Button (thanks, Samsung)
|
||||||
|
if (!(event == null || event.getAction() == KeyEvent.ACTION_DOWN))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Enter clicked on Bluetooth Keyboard (action is zero on hardware keyboards)
|
||||||
|
if (action == EditorInfo.IME_ACTION_UNSPECIFIED && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return action == EditorInfo.IME_ACTION_DONE
|
||||||
|
|| action == EditorInfo.IME_ACTION_GO
|
||||||
|
|| action == EditorInfo.IME_ACTION_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesRequiredPINLength(String input) {
|
||||||
|
final PINInputView inputView = this.inputView.get();
|
||||||
|
|
||||||
|
if (inputView == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return inputView.matchesRequiredPINLength(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextEntered(int count) {
|
||||||
|
if (count == Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()))) {
|
||||||
|
var inputView = this.inputView.get();
|
||||||
|
InputEventListener eventListener = this.eventListener.get();
|
||||||
|
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onInputEntered(inputView.getText());
|
||||||
|
|
||||||
|
inputView.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface InputEventListener {
|
||||||
|
void onInputEntered(String input);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package com.guardanis.applock.pin;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import ru.vtosters.hooks.other.ThemesUtils;
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class PINInputView extends LinearLayout implements TextWatcher {
|
||||||
|
|
||||||
|
private final WeakHashMap<PINItemView, PINItemAnimator> animators = new WeakHashMap<PINItemView, PINItemAnimator>();
|
||||||
|
private Paint itemTextPaint;
|
||||||
|
private Paint itemBackgroundPaint;
|
||||||
|
private int inputViewsCount = 10;
|
||||||
|
private PINItemView[] pinItemViews;
|
||||||
|
private EditText editText;
|
||||||
|
private String lastText = "";
|
||||||
|
private boolean passwordCharactersEnabled = true;
|
||||||
|
private String passwordCharacter;
|
||||||
|
private InputTextListener listener;
|
||||||
|
|
||||||
|
|
||||||
|
public PINInputView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
init(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(AttributeSet attrs) {
|
||||||
|
setWillNotDraw(false);
|
||||||
|
setupEditText();
|
||||||
|
|
||||||
|
|
||||||
|
itemTextPaint = new Paint();
|
||||||
|
itemTextPaint.setColor(ThemesUtils.getAccentColor());
|
||||||
|
|
||||||
|
itemBackgroundPaint = new Paint();
|
||||||
|
itemBackgroundPaint.setColor(ThemesUtils.getMutedAccentColor());
|
||||||
|
|
||||||
|
|
||||||
|
inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()));
|
||||||
|
|
||||||
|
passwordCharacter = getResources().getString(AndroidUtils.getIdentifier("applock__password_char", "string"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupEditText() {
|
||||||
|
removeAllViews();
|
||||||
|
|
||||||
|
this.editText = inflateFakeEditText();
|
||||||
|
this.editText.addTextChangedListener(this);
|
||||||
|
|
||||||
|
addView(editText);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EditText inflateFakeEditText() {
|
||||||
|
return (EditText) LayoutInflater.from(getContext())
|
||||||
|
.inflate(AndroidUtils.getIdentifier("applock__fake_edit_text", "layout"), this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputView setInputViewsCount(int inputViewsCount) {
|
||||||
|
this.inputViewsCount = inputViewsCount;
|
||||||
|
|
||||||
|
return reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputView reset() {
|
||||||
|
this.lastText = "";
|
||||||
|
this.editText.setText("");
|
||||||
|
|
||||||
|
if (pinItemViews != null)
|
||||||
|
animateLastOut();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
ensureKeyboardVisible();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureKeyboardVisible() {
|
||||||
|
editText.requestFocus();
|
||||||
|
|
||||||
|
((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
|
||||||
|
.showSoftInput(editText, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
if (pinItemViews == null || pinItemViews.length != inputViewsCount)
|
||||||
|
setupItemViews(canvas);
|
||||||
|
|
||||||
|
String text = editText.getText().toString();
|
||||||
|
|
||||||
|
for (int i = 0; i < pinItemViews.length; i++) {
|
||||||
|
String textItem = i < text.length()
|
||||||
|
? (passwordCharactersEnabled ? passwordCharacter : text.substring(i, i + 1))
|
||||||
|
: "";
|
||||||
|
|
||||||
|
pinItemViews[i].draw(canvas, textItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupItemViews(Canvas canvas) {
|
||||||
|
pinItemViews = new PINItemView[inputViewsCount];
|
||||||
|
|
||||||
|
int cellWidth = canvas.getWidth() / inputViewsCount;
|
||||||
|
int largestRadius = Math.min(cellWidth / 2, canvas.getHeight() / 2);
|
||||||
|
int smallestRadius = (int) (largestRadius * Float.parseFloat(getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string"))));
|
||||||
|
int[] minMaxRadius = new int[]{smallestRadius, largestRadius};
|
||||||
|
|
||||||
|
itemTextPaint.setTextSize((int) (largestRadius * .85));
|
||||||
|
|
||||||
|
for (int i = 0; i < pinItemViews.length; i++) {
|
||||||
|
float[] positionInCanvas = getPositionInCanvas(canvas, i, cellWidth);
|
||||||
|
|
||||||
|
pinItemViews[i] = new PINItemView(positionInCanvas, minMaxRadius, itemTextPaint, itemBackgroundPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float[] getPositionInCanvas(Canvas canvas, int position, int cellWidth) {
|
||||||
|
return new float[]{
|
||||||
|
(cellWidth * position) + (cellWidth / 2),
|
||||||
|
canvas.getHeight() / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
if (editable == null || lastText == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String text = editable.toString();
|
||||||
|
if (inputViewsCount < text.length()) {
|
||||||
|
editText.setText(lastText);
|
||||||
|
editText.setSelection(editText.getText().length());
|
||||||
|
} else if (text.length() < lastText.length()) {
|
||||||
|
animateLastOut();
|
||||||
|
this.lastText = text;
|
||||||
|
} else if (lastText.length() < text.length()) {
|
||||||
|
animateLastIn();
|
||||||
|
this.lastText = text;
|
||||||
|
}
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onTextEntered(text.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputTextListener(InputTextListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateLastOut() {
|
||||||
|
int startingIndex = editText.getText().toString().length(); // One after current length, only happens on backspace
|
||||||
|
|
||||||
|
for (int i = pinItemViews.length - 1; startingIndex <= i; i--)
|
||||||
|
if (!pinItemViews[i].isAnimatedOut())
|
||||||
|
animate(pinItemViews[i], PINItemAnimator.ItemAnimationDirection.OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateLastIn() {
|
||||||
|
PINItemView item = pinItemViews[editText.getText().toString().length() - 1];
|
||||||
|
animate(item, PINItemAnimator.ItemAnimationDirection.IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animate(PINItemView view, PINItemAnimator.ItemAnimationDirection direction) {
|
||||||
|
cancelPreviousAnimation(view);
|
||||||
|
view.setAnimationDirection(direction);
|
||||||
|
|
||||||
|
PINItemAnimator animator = new PINItemAnimator(this, view, direction);
|
||||||
|
animators.put(view, animator);
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelPreviousAnimation(PINItemView view) {
|
||||||
|
PINItemAnimator animator = animators.get(view);
|
||||||
|
|
||||||
|
try {
|
||||||
|
animator.cancel();
|
||||||
|
animators.put(view, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return editText.getText().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnEditorActionListener(TextView.OnEditorActionListener actionListener) {
|
||||||
|
editText.setOnEditorActionListener(actionListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPasswordCharactersEnabled(boolean passwordCharactersEnabled) {
|
||||||
|
this.passwordCharactersEnabled = passwordCharactersEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPasswordCharacter(String passwordCharacter) {
|
||||||
|
this.passwordCharacter = passwordCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matchesRequiredPINLength(String input) {
|
||||||
|
return input.length() == inputViewsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.guardanis.applock.pin;
|
||||||
|
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
import android.view.animation.Interpolator;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class PINItemAnimator extends Thread {
|
||||||
|
|
||||||
|
private static final int ANIMATION_DURATION = 150;
|
||||||
|
private static final int UPDATE_RATE = 25;
|
||||||
|
private final WeakReference<PINInputView> inputView;
|
||||||
|
private final PINItemView itemView;
|
||||||
|
private final ItemAnimationDirection animationDirection;
|
||||||
|
private final Interpolator itemInterpolator = new AccelerateDecelerateInterpolator();
|
||||||
|
private final float minSizePercent;
|
||||||
|
private long startTime;
|
||||||
|
private boolean canceled = false;
|
||||||
|
|
||||||
|
public PINItemAnimator(PINInputView inputView, PINItemView itemView, ItemAnimationDirection animationDirection) {
|
||||||
|
this.inputView = new WeakReference<>(inputView);
|
||||||
|
this.itemView = itemView;
|
||||||
|
this.animationDirection = animationDirection;
|
||||||
|
this.minSizePercent = Float.parseFloat(inputView.getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
this.startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (animationDirection == ItemAnimationDirection.IN)
|
||||||
|
animateIn();
|
||||||
|
else
|
||||||
|
animateOut();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateIn() throws Exception {
|
||||||
|
float percent = minSizePercent;
|
||||||
|
|
||||||
|
while (percent < 1 && !canceled) {
|
||||||
|
percent = Math.min(minSizePercent + calculatePercentComplete(), 1);
|
||||||
|
updateView(percent);
|
||||||
|
|
||||||
|
Thread.sleep(UPDATE_RATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateOut() throws Exception {
|
||||||
|
float percent = 1 - calculatePercentComplete();
|
||||||
|
|
||||||
|
while (minSizePercent < percent && !canceled) {
|
||||||
|
percent = Math.max(1 - calculatePercentComplete(), minSizePercent);
|
||||||
|
updateView(percent);
|
||||||
|
|
||||||
|
Thread.sleep(UPDATE_RATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float calculatePercentComplete() {
|
||||||
|
float completed = ((float) (System.currentTimeMillis() - startTime)) / ANIMATION_DURATION;
|
||||||
|
|
||||||
|
return itemInterpolator.getInterpolation(completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateView(final float percent) {
|
||||||
|
final PINInputView inputView = this.inputView.get();
|
||||||
|
|
||||||
|
if (inputView == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
inputView.post(() -> {
|
||||||
|
itemView.onAnimationUpdate(percent);
|
||||||
|
|
||||||
|
inputView.invalidate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
this.canceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ItemAnimationDirection {
|
||||||
|
IN, OUT
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.guardanis.applock.pin;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
|
||||||
|
public class PINItemView {
|
||||||
|
|
||||||
|
private final float[] position;
|
||||||
|
private final int intendedRadius;
|
||||||
|
private int currentRadius;
|
||||||
|
private Paint backgroundPaint;
|
||||||
|
|
||||||
|
private float[] textPosition;
|
||||||
|
private Paint textPaint;
|
||||||
|
|
||||||
|
private PINItemAnimator.ItemAnimationDirection animationDirection = PINItemAnimator.ItemAnimationDirection.IN;
|
||||||
|
|
||||||
|
public PINItemView(float[] position, int[] minMaxRadius, Paint baseTextPaint, Paint baseBackgroundPaint) {
|
||||||
|
this.position = position;
|
||||||
|
this.intendedRadius = minMaxRadius[1];
|
||||||
|
this.currentRadius = minMaxRadius[0];
|
||||||
|
|
||||||
|
setupPaints(baseTextPaint, baseBackgroundPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupPaints(Paint baseTextPaint, Paint baseBackgroundPaint) {
|
||||||
|
this.textPaint = new Paint();
|
||||||
|
this.textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
this.textPaint.setAntiAlias(true);
|
||||||
|
this.textPaint.setColor(baseTextPaint.getColor());
|
||||||
|
this.textPaint.setTextSize(baseTextPaint.getTextSize());
|
||||||
|
|
||||||
|
this.textPosition = new float[]{
|
||||||
|
position[0],
|
||||||
|
position[1] - ((textPaint.descent() + textPaint.ascent()) / 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
this.backgroundPaint = new Paint();
|
||||||
|
this.backgroundPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
this.backgroundPaint.setAntiAlias(true);
|
||||||
|
this.backgroundPaint.setColor(baseBackgroundPaint.getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Canvas canvas, String textValue) {
|
||||||
|
canvas.drawCircle(position[0], position[1], currentRadius, backgroundPaint);
|
||||||
|
canvas.drawText(textValue, textPosition[0], textPosition[1], textPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnimationDirection(PINItemAnimator.ItemAnimationDirection animationDirection) {
|
||||||
|
this.animationDirection = animationDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAnimationUpdate(float percentCompleted) {
|
||||||
|
this.currentRadius = (int) (intendedRadius * percentCompleted);
|
||||||
|
this.textPaint.setAlpha((int) (255 * percentCompleted));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnimatedOut() {
|
||||||
|
return animationDirection == PINItemAnimator.ItemAnimationDirection.OUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package com.guardanis.applock.services;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
|
import androidx.core.os.CancellationSignal;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.utils.CipherGenerator;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class FingerprintLockService extends LockService {
|
||||||
|
|
||||||
|
private static final String PREF_ENROLLMENT_ALLOWED = "pin__fingerprint_enrollment_allowed";
|
||||||
|
protected CancellationSignal fingerprintCancellationSignal;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnrollmentEligible(Context context) {
|
||||||
|
return Build.VERSION_CODES.M <= Build.VERSION.SDK_INT && context.getResources().getBoolean(AndroidUtils.getIdentifier("applock__fingerprint_service_enabled", "bool")) && isHardwarePresent(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enroll(Context context, AuthenticationDelegate delegate) {
|
||||||
|
authenticate(context, false, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticate(Context context, AuthenticationDelegate delegate) {
|
||||||
|
authenticate(context, true, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void authenticate(Context context, boolean localEnrollmentRequired, AuthenticationDelegate delegate) {
|
||||||
|
int errorCode = getRequiredResolutionErrorCode(context, localEnrollmentRequired);
|
||||||
|
|
||||||
|
if (-1 < errorCode) {
|
||||||
|
delegate.onResolutionRequired(errorCode);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be handled by getRequiredResolutionErrorCode(Context, boolean)
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
|
||||||
|
|
||||||
|
attemptFingerprintManagerAuthentication(context, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the resolvable error code or -1 if there are no issues requiring a resolution
|
||||||
|
*/
|
||||||
|
protected int getRequiredResolutionErrorCode(Context context, boolean localEnrollmentRequired) {
|
||||||
|
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
|
return AppLock.ERROR_CODE_SDK_VERSION_MINIMUM;
|
||||||
|
|
||||||
|
if (localEnrollmentRequired && !isEnrolled(context))
|
||||||
|
return AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED;
|
||||||
|
|
||||||
|
if (!isHardwarePresent(context)) return AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE;
|
||||||
|
|
||||||
|
if (!manager.hasEnrolledFingerprints()) return AppLock.ERROR_CODE_FINGERPRINTS_EMPTY;
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED)
|
||||||
|
return AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void attemptFingerprintManagerAuthentication(final Context context, final AuthenticationDelegate delegate) {
|
||||||
|
this.fingerprintCancellationSignal = new CancellationSignal();
|
||||||
|
|
||||||
|
|
||||||
|
var callback = new FingerprintManagerCompat.AuthenticationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errMsgId, CharSequence errString) {
|
||||||
|
super.onAuthenticationError(errMsgId, errString);
|
||||||
|
|
||||||
|
delegate.onResolutionRequired(AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
|
||||||
|
super.onAuthenticationHelp(helpMsgId, helpString);
|
||||||
|
|
||||||
|
delegate.onAuthenticationHelp(helpMsgId, helpString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
|
||||||
|
super.onAuthenticationSucceeded(result);
|
||||||
|
|
||||||
|
notifyEnrolled(context);
|
||||||
|
|
||||||
|
delegate.onAuthenticationSuccess(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
super.onAuthenticationFailed();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
delegate.onAuthenticating(fingerprintCancellationSignal);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cipher cipher = generateAuthCipher(context, false, 0);
|
||||||
|
FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
|
||||||
|
|
||||||
|
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
|
||||||
|
manager.authenticate(cryptoObject, 0, fingerprintCancellationSignal, callback, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
delegate.onResolutionRequired(AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHardwarePresent(Context context) {
|
||||||
|
return FingerprintManagerCompat.from(context).isHardwareDetected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnrolled(Context context) {
|
||||||
|
return AppLock.getInstance(context).getPreferences().getBoolean(PREF_ENROLLMENT_ALLOWED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyEnrolled(Context context) {
|
||||||
|
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, true).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
protected Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
|
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
|
||||||
|
|
||||||
|
return new CipherGenerator().generateAuthCipher(context, forceRegenerate, attempts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateEnrollments(Context context) {
|
||||||
|
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, false).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelPendingAuthentications(Context context) {
|
||||||
|
if (fingerprintCancellationSignal != null) {
|
||||||
|
this.fingerprintCancellationSignal.cancel();
|
||||||
|
this.fingerprintCancellationSignal = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AuthenticationDelegate {
|
||||||
|
void onResolutionRequired(int errorCode);
|
||||||
|
|
||||||
|
void onAuthenticationHelp(int code, CharSequence message);
|
||||||
|
|
||||||
|
void onAuthenticating(CancellationSignal cancellationSignal);
|
||||||
|
|
||||||
|
void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result);
|
||||||
|
|
||||||
|
void onAuthenticationFailed(String message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.guardanis.applock.services;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
public abstract class LockService {
|
||||||
|
|
||||||
|
public abstract boolean isEnrolled(Context context);
|
||||||
|
|
||||||
|
public abstract boolean isEnrollmentEligible(Context context);
|
||||||
|
|
||||||
|
public abstract void cancelPendingAuthentications(Context context);
|
||||||
|
|
||||||
|
public abstract void invalidateEnrollments(Context context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.guardanis.applock.services;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.utils.CryptoUtils;
|
||||||
|
|
||||||
|
public class PINLockService extends LockService {
|
||||||
|
|
||||||
|
private static final String PREF_SAVED_LOCKED_PASSWORD = "pin__saved_locked_password";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnrollmentEligible(Context context) {
|
||||||
|
// I couldn't find a good reason to allow the disabling of the PIN as an option.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticate(Context context, String pin, AuthenticationDelegate eventListener) {
|
||||||
|
if (!isEnrolled(context)) {
|
||||||
|
eventListener.onNoPIN();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getEnrolledPIN(context).equals(CryptoUtils.encryptSha1(pin))) {
|
||||||
|
eventListener.onPINDoesNotMatch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventListener.onPINMatches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnrolled(Context context) {
|
||||||
|
return AppLock.getInstance(context)
|
||||||
|
.getPreferences()
|
||||||
|
.getString(PREF_SAVED_LOCKED_PASSWORD, null) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEnrolledPIN(Context context) {
|
||||||
|
return AppLock.getInstance(context)
|
||||||
|
.getPreferences()
|
||||||
|
.getString(PREF_SAVED_LOCKED_PASSWORD, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enroll(Context context, String pin) {
|
||||||
|
AppLock.getInstance(context)
|
||||||
|
.getPreferences()
|
||||||
|
.edit()
|
||||||
|
.putString(PREF_SAVED_LOCKED_PASSWORD, CryptoUtils.encryptSha1(pin))
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateEnrollments(Context context) {
|
||||||
|
AppLock.getInstance(context)
|
||||||
|
.getPreferences()
|
||||||
|
.edit()
|
||||||
|
.remove(PREF_SAVED_LOCKED_PASSWORD)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelPendingAuthentications(Context context) {
|
||||||
|
// There are none
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface AuthenticationDelegate {
|
||||||
|
void onNoPIN();
|
||||||
|
|
||||||
|
void onPINDoesNotMatch();
|
||||||
|
|
||||||
|
void onPINMatches();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.guardanis.applock.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
|
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||||
|
import android.security.keystore.KeyProperties;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.UnrecoverableKeyException;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
public class CipherGenerator {
|
||||||
|
|
||||||
|
private static final String KEYSTORE_NAME = "AndroidKeyStore";
|
||||||
|
|
||||||
|
public Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
|
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
|
||||||
|
|
||||||
|
String alias = context.getString(AndroidUtils.getIdentifier("applock__fingerprint_alias", "string"));
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
|
||||||
|
keyStore.load(null);
|
||||||
|
|
||||||
|
if (forceRegenerate || !keyStore.containsAlias(alias)) {
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
|
||||||
|
keyGenerator.init(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
||||||
|
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||||
|
.setUserAuthenticationRequired(true)
|
||||||
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
keyGenerator.generateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
String cipherFormat = String.format(
|
||||||
|
"%s/%s/%s",
|
||||||
|
KeyProperties.KEY_ALGORITHM_AES,
|
||||||
|
KeyProperties.BLOCK_MODE_CBC,
|
||||||
|
KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(cipherFormat);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, keyStore.getKey(alias, null));
|
||||||
|
|
||||||
|
return cipher;
|
||||||
|
} catch (KeyPermanentlyInvalidatedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
if (1 < attempts)
|
||||||
|
return generateAuthCipher(context, true, attempts + 1);
|
||||||
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
|
||||||
|
if (1 < attempts)
|
||||||
|
return generateAuthCipher(context, true, attempts + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.guardanis.applock.utils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
public class CryptoUtils {
|
||||||
|
|
||||||
|
public static String encryptSha1(String text) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||||
|
md.update(text.getBytes(StandardCharsets.UTF_8), 0, text.length());
|
||||||
|
|
||||||
|
return convertToHex(md.digest());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String convertToHex(byte[] data) {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
|
||||||
|
for (byte b : data) {
|
||||||
|
int half = (b >>> 4) & 0x0F;
|
||||||
|
int twoHalves = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
buf.append(half <= 9
|
||||||
|
? (char) ('0' + half)
|
||||||
|
: (char) ('a' + half - 10));
|
||||||
|
|
||||||
|
half = b & 0x0F;
|
||||||
|
}
|
||||||
|
while (twoHalves++ < 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.guardanis.applock.utils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public class LifeCycleUtils {
|
||||||
|
|
||||||
|
public static AppLockActivityLifeCycleCallbacks attach(Activity activity, AppLockActivityLifeCycleCallbacks.Delegate delegate) {
|
||||||
|
AppLockActivityLifeCycleCallbacks callbacks = new AppLockActivityLifeCycleCallbacks(activity, delegate);
|
||||||
|
|
||||||
|
activity.getApplication()
|
||||||
|
.registerActivityLifecycleCallbacks(callbacks);
|
||||||
|
|
||||||
|
return callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AppLockActivityLifeCycleCallbacks implements Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
|
protected WeakReference<Activity> openedActivity;
|
||||||
|
protected WeakReference<Delegate> delegate;
|
||||||
|
|
||||||
|
public AppLockActivityLifeCycleCallbacks(Activity activity, Delegate delegate) {
|
||||||
|
this.openedActivity = new WeakReference<>(activity);
|
||||||
|
this.delegate = new WeakReference<>(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityStarted(Activity activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityStopped(Activity activity) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResumed(Activity activity) {
|
||||||
|
Activity opened = this.openedActivity.get();
|
||||||
|
Delegate delegate = this.delegate.get();
|
||||||
|
|
||||||
|
if (opened == null || delegate == null || opened != activity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delegate.onActivityResumed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityPaused(Activity activity) {
|
||||||
|
Activity opened = this.openedActivity.get();
|
||||||
|
Delegate delegate = this.delegate.get();
|
||||||
|
|
||||||
|
if (opened == null || delegate == null || opened != activity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
delegate.onActivityPaused();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityDestroyed(Activity activity) {
|
||||||
|
Activity opened = this.openedActivity.get();
|
||||||
|
|
||||||
|
if (opened == null || opened != activity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
opened.getApplication()
|
||||||
|
.unregisterActivityLifecycleCallbacks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Delegate {
|
||||||
|
void onActivityResumed();
|
||||||
|
|
||||||
|
void onActivityPaused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
package com.guardanis.applock.views;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.pin.PINInputController;
|
||||||
|
import com.guardanis.applock.pin.PINInputView;
|
||||||
|
import com.guardanis.applock.utils.LifeCycleUtils;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public abstract class AppLockViewController implements LifeCycleUtils.AppLockActivityLifeCycleCallbacks.Delegate {
|
||||||
|
|
||||||
|
protected PINInputController pinInputController;
|
||||||
|
|
||||||
|
protected WeakReference<Activity> activity;
|
||||||
|
protected WeakReference<View> parent;
|
||||||
|
|
||||||
|
protected WeakReference<PINInputView> pinInputView;
|
||||||
|
|
||||||
|
protected WeakReference<TextView> descriptionView;
|
||||||
|
protected WeakReference<View> actionSettings;
|
||||||
|
|
||||||
|
protected boolean autoAuthorizationEnabled = true;
|
||||||
|
|
||||||
|
protected Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
|
||||||
|
|
||||||
|
public AppLockViewController(Activity activity, View parent) {
|
||||||
|
this.activity = new WeakReference<>(activity);
|
||||||
|
this.parent = new WeakReference<>(parent);
|
||||||
|
this.descriptionView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__description", "id")));
|
||||||
|
this.actionSettings = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__action_settings", "id")));
|
||||||
|
|
||||||
|
this.pinInputView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__input_view", "id")));
|
||||||
|
|
||||||
|
int inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(activity));
|
||||||
|
|
||||||
|
boolean passwordCharsEnabled = parent.getResources()
|
||||||
|
.getBoolean(AndroidUtils.getIdentifier("applock__item_password_chars_enabled", "bool"));
|
||||||
|
|
||||||
|
this.pinInputController = new PINInputController(pinInputView.get())
|
||||||
|
.setInputNumbersCount(inputViewsCount)
|
||||||
|
.setPasswordCharactersEnabled(passwordCharsEnabled);
|
||||||
|
|
||||||
|
this.activityLifecycleCallbacks = LifeCycleUtils.attach(activity, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void setupRootFlow();
|
||||||
|
|
||||||
|
public void setDescription(int descriptionResId) {
|
||||||
|
final TextView descriptionView = this.descriptionView.get();
|
||||||
|
|
||||||
|
if (descriptionView == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
descriptionView.setText(descriptionResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
final TextView descriptionView = this.descriptionView.get();
|
||||||
|
|
||||||
|
if (descriptionView == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
descriptionView.setText(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T extends View> void hide(WeakReference<T> weakView) {
|
||||||
|
final T view = weakView.get();
|
||||||
|
|
||||||
|
if (view == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T extends View> void show(WeakReference<T> weakView) {
|
||||||
|
final T view = weakView.get();
|
||||||
|
|
||||||
|
if (view == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PINInputController getPINInputController() {
|
||||||
|
return pinInputController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getParent() {
|
||||||
|
return parent.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoAuthorizationEnabled(boolean autoAuthorizationEnabled) {
|
||||||
|
this.autoAuthorizationEnabled = autoAuthorizationEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateActionSettings(final int errorCode) {
|
||||||
|
View actionSettings = this.actionSettings.get();
|
||||||
|
|
||||||
|
if (actionSettings == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (getSettingsIntent(errorCode) == null) {
|
||||||
|
actionSettings.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actionSettings.setVisibility(View.VISIBLE);
|
||||||
|
actionSettings.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View view) {
|
||||||
|
handleActionSettingsClicked(errorCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void handleActionSettingsClicked(int errorCode);
|
||||||
|
|
||||||
|
protected void handleInitialErrorPrompt(int errorCode) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (errorCode == AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED) {
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
new String[]{Manifest.permission.USE_FINGERPRINT},
|
||||||
|
AppLock.REQUEST_CODE_FINGERPRINT_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getDescriptionResIdForError(int errorCode) {
|
||||||
|
return switch (errorCode) {
|
||||||
|
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED ->
|
||||||
|
AndroidUtils.getIdentifier("applock__fingerprint_error_permission", "string");
|
||||||
|
case AppLock.ERROR_CODE_FINGERPRINTS_EMPTY ->
|
||||||
|
AndroidUtils.getIdentifier("applock__fingerprint_error_none", "string");
|
||||||
|
case AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
|
||||||
|
AndroidUtils.getIdentifier("applock__fingerprint_error_hardware", "string");
|
||||||
|
case AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED ->
|
||||||
|
AndroidUtils.getIdentifier("applock__fingerprint_error_not_enrolled", "string");
|
||||||
|
default -> AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an Intent directed towards the correct system setting for the error, or null if there is none.
|
||||||
|
*/
|
||||||
|
public Intent getSettingsIntent(int errorCode) {
|
||||||
|
return switch (errorCode) {
|
||||||
|
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED, AppLock.ERROR_CODE_FINGERPRINTS_EMPTY, AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
|
||||||
|
new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterReceivers() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
activity.getApplication()
|
||||||
|
.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResumed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityPaused() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
package com.guardanis.applock.views;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||||
|
import androidx.core.os.CancellationSignal;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.pin.PINInputController;
|
||||||
|
import com.guardanis.applock.services.FingerprintLockService;
|
||||||
|
import com.guardanis.applock.services.PINLockService;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class LockCreationViewController extends AppLockViewController
|
||||||
|
implements PINInputController.InputEventListener, FingerprintLockService.AuthenticationDelegate {
|
||||||
|
|
||||||
|
protected DisplayVariant displayVariant = DisplayVariant.NONE;
|
||||||
|
protected WeakReference<Delegate> delegate;
|
||||||
|
protected WeakReference<View> chooserParent;
|
||||||
|
protected String pinFirst;
|
||||||
|
|
||||||
|
public LockCreationViewController(Activity activity, View parent) {
|
||||||
|
super(activity, parent);
|
||||||
|
|
||||||
|
this.chooserParent = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__create_chooser_items", "id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockCreationViewController setDelegate(Delegate delegate) {
|
||||||
|
this.delegate = new WeakReference<>(delegate);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupRootFlow() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setupPINCreation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupPINCreation() {
|
||||||
|
this.displayVariant = DisplayVariant.PIN_CREATION;
|
||||||
|
|
||||||
|
hide(chooserParent);
|
||||||
|
hide(actionSettings);
|
||||||
|
|
||||||
|
show(pinInputView);
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin", "string"));
|
||||||
|
|
||||||
|
pinInputController.ensureKeyboardVisible();
|
||||||
|
pinInputController.setInputEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupPINConfirmation() {
|
||||||
|
this.displayVariant = DisplayVariant.PIN_CONFIRMATION;
|
||||||
|
|
||||||
|
hide(chooserParent);
|
||||||
|
hide(actionSettings);
|
||||||
|
|
||||||
|
show(pinInputView);
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_confirm", "string"));
|
||||||
|
|
||||||
|
pinInputController.ensureKeyboardVisible();
|
||||||
|
pinInputController.setInputEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInputEntered(String input) {
|
||||||
|
switch (displayVariant) {
|
||||||
|
case PIN_CREATION -> {
|
||||||
|
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.pinFirst = input;
|
||||||
|
setupPINConfirmation();
|
||||||
|
}
|
||||||
|
case PIN_CONFIRMATION -> {
|
||||||
|
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!input.equals(pinFirst)) {
|
||||||
|
this.pinFirst = null;
|
||||||
|
|
||||||
|
setupPINCreation();
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin_reattempt", "string"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createPINLock(input);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createPINLock(String input) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.getLockService(PINLockService.class)
|
||||||
|
.enroll(activity, input);
|
||||||
|
|
||||||
|
var fingerprintLockService = AppLock.getInstance(activity).getLockService(FingerprintLockService.class);
|
||||||
|
if (fingerprintLockService.isEnrollmentEligible(activity)) {
|
||||||
|
setupFingerprintAuthentication();
|
||||||
|
} else {
|
||||||
|
handleLockCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupFingerprintAuthentication() {
|
||||||
|
this.displayVariant = DisplayVariant.FINGERPRINT_AUTHENTICATION;
|
||||||
|
|
||||||
|
hide(pinInputView);
|
||||||
|
hide(chooserParent);
|
||||||
|
hide(actionSettings);
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
|
||||||
|
|
||||||
|
if (autoAuthorizationEnabled)
|
||||||
|
attemptFingerprintAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void attemptFingerprintAuthentication() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.getLockService(FingerprintLockService.class)
|
||||||
|
.enroll(activity, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResolutionRequired(int errorCode) {
|
||||||
|
setDescription(getDescriptionResIdForError(errorCode));
|
||||||
|
updateActionSettings(errorCode);
|
||||||
|
handleInitialErrorPrompt(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int code, CharSequence message) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
|
||||||
|
String formatted = String.format(unformattedHelpMessage, message);
|
||||||
|
|
||||||
|
setDescription(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticating(CancellationSignal cancellationSignal) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result) {
|
||||||
|
handleLockCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed(String message) {
|
||||||
|
setDescription(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleLockCreated() {
|
||||||
|
this.displayVariant = DisplayVariant.NONE;
|
||||||
|
|
||||||
|
Delegate delegate = this.delegate.get();
|
||||||
|
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.onLockCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityPaused() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.cancelPendingAuthentications();
|
||||||
|
|
||||||
|
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResumed() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
|
||||||
|
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
|
||||||
|
hide(actionSettings);
|
||||||
|
|
||||||
|
attemptFingerprintAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleActionSettingsClicked(int errorCode) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
Intent intent = getSettingsIntent(errorCode);
|
||||||
|
|
||||||
|
if (activity == null || intent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DisplayVariant {
|
||||||
|
NONE,
|
||||||
|
PIN_CREATION,
|
||||||
|
PIN_CONFIRMATION,
|
||||||
|
FINGERPRINT_AUTHENTICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Delegate {
|
||||||
|
void onLockCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
package com.guardanis.applock.views;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.guardanis.applock.AppLock;
|
||||||
|
import com.guardanis.applock.pin.PINInputController;
|
||||||
|
import com.guardanis.applock.services.FingerprintLockService;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
|
public class UnlockViewController extends AppLockViewController implements AppLock.UnlockDelegate, PINInputController.InputEventListener {
|
||||||
|
|
||||||
|
protected DisplayVariant displayVariant = DisplayVariant.PIN_UNLOCK;
|
||||||
|
protected WeakReference<Delegate> delegate;
|
||||||
|
|
||||||
|
public UnlockViewController(Activity activity, View parent) {
|
||||||
|
super(activity, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnlockViewController setDelegate(Delegate delegate) {
|
||||||
|
this.delegate = new WeakReference<Delegate>(delegate);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupRootFlow() {
|
||||||
|
View parent = this.parent.get();
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setupPINUnlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupPINUnlock() {
|
||||||
|
this.displayVariant = DisplayVariant.PIN_UNLOCK;
|
||||||
|
|
||||||
|
hide(actionSettings);
|
||||||
|
show(pinInputView);
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_pin", "string"));
|
||||||
|
|
||||||
|
requestOpenKeyboard();
|
||||||
|
pinInputController.setInputEventListener(this);
|
||||||
|
|
||||||
|
FingerprintLockService fingerprintService = AppLock.getInstance(this.parent.get().getContext())
|
||||||
|
.getLockService(FingerprintLockService.class);
|
||||||
|
|
||||||
|
if (fingerprintService.isEnrolled(parent.get().getContext()))
|
||||||
|
attemptFingerprintAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInputEntered(String input) {
|
||||||
|
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attemptPINUnlock(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void attemptPINUnlock(String input) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.attemptPINUnlock(input, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void attemptFingerprintAuthentication() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.attemptFingerprintUnlock(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnlockSuccessful() {
|
||||||
|
this.displayVariant = DisplayVariant.NONE;
|
||||||
|
|
||||||
|
Delegate delegate = this.delegate.get();
|
||||||
|
|
||||||
|
if (delegate != null)
|
||||||
|
delegate.onUnlockSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResolutionRequired(int errorCode) {
|
||||||
|
setDescription(getDescriptionResIdForError(errorCode));
|
||||||
|
updateActionSettings(errorCode);
|
||||||
|
handleInitialErrorPrompt(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int code, String message) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
|
||||||
|
String formatted = String.format(unformattedHelpMessage, message);
|
||||||
|
|
||||||
|
setDescription(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailureLimitExceeded(String message) {
|
||||||
|
setDescription(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestOpenKeyboard() {
|
||||||
|
pinInputController.ensureKeyboardVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityPaused() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AppLock.getInstance(activity)
|
||||||
|
.cancelPendingAuthentications();
|
||||||
|
|
||||||
|
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResumed() {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
|
||||||
|
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
|
||||||
|
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint", "string"));
|
||||||
|
hide(actionSettings);
|
||||||
|
|
||||||
|
attemptFingerprintAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleActionSettingsClicked(int errorCode) {
|
||||||
|
Activity activity = this.activity.get();
|
||||||
|
Intent intent = getSettingsIntent(errorCode);
|
||||||
|
|
||||||
|
if (activity == null || intent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
activity.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DisplayVariant {
|
||||||
|
NONE,
|
||||||
|
PIN_UNLOCK,
|
||||||
|
FINGERPRINT_AUTHENTICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Delegate {
|
||||||
|
void onUnlockSuccessful();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.vk.api.audio;
|
package com.vk.api.audio;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.vk.api.base.ApiRequest;
|
import com.vk.api.base.ApiRequest;
|
||||||
import com.vk.dto.music.MusicTrack;
|
import com.vk.dto.music.MusicTrack;
|
||||||
import com.vk.log.L;
|
import com.vk.log.L;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
import ru.vtosters.hooks.other.Preferences;
|
||||||
import ru.vtosters.lite.music.Genius;
|
import ru.vtosters.lite.music.Genius;
|
||||||
@@ -12,6 +15,9 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import ru.vtosters.lite.music.Genius;
|
||||||
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
public class AudioGetLyrics extends ApiRequest<AudioGetLyrics.a> {
|
public class AudioGetLyrics extends ApiRequest<AudioGetLyrics.a> {
|
||||||
private final MusicTrack musicTrack;
|
private final MusicTrack musicTrack;
|
||||||
|
|
||||||
@@ -25,7 +31,7 @@ public class AudioGetLyrics extends ApiRequest<AudioGetLyrics.a> {
|
|||||||
public a a(@NonNull JSONObject jSONObject) {
|
public a a(@NonNull JSONObject jSONObject) {
|
||||||
var aVar = new a();
|
var aVar = new a();
|
||||||
|
|
||||||
if (Preferences.getBoolValue("useGenius", false) && !Preferences.serverFeaturesDisable()) {
|
if (Preferences.getBoolValue("useGenius", false)) {
|
||||||
aVar.a = Genius.getTextMusic(musicTrack);
|
aVar.a = Genius.getTextMusic(musicTrack);
|
||||||
return aVar;
|
return aVar;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ import org.json.JSONException;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class GiftGetByStickerId extends ApiRequest<GiftGetByStickerId.a> {
|
public class GiftGetByStickerId extends ApiRequest<GiftGetByStickerId.a> {
|
||||||
|
public static class a {
|
||||||
|
public final int a;
|
||||||
|
public final CatalogedGift b;
|
||||||
|
|
||||||
|
public a(int i, CatalogedGift catalogedGift) {
|
||||||
|
this.a = i;
|
||||||
|
this.b = catalogedGift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public GiftGetByStickerId(Context context, int i) {
|
public GiftGetByStickerId(Context context, int i) {
|
||||||
super("execute.getGiftByStickerId");
|
super("execute.getGiftByStickerId");
|
||||||
b("sticker_id", i);
|
b("sticker_id", i);
|
||||||
@@ -20,14 +30,4 @@ public class GiftGetByStickerId extends ApiRequest<GiftGetByStickerId.a> {
|
|||||||
return new a(jSONObject2.optInt("balance"),
|
return new a(jSONObject2.optInt("balance"),
|
||||||
new CatalogedGift(jSONObject2.getJSONObject("gift")));
|
new CatalogedGift(jSONObject2.getJSONObject("gift")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class a {
|
|
||||||
public final int a;
|
|
||||||
public final CatalogedGift b;
|
|
||||||
|
|
||||||
public a(int i, CatalogedGift catalogedGift) {
|
|
||||||
this.a = i;
|
|
||||||
this.b = catalogedGift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,15 +27,6 @@ public class RecoloredDrawable extends Drawable implements Drawable.Callback {
|
|||||||
onStateChange(new int[0]);
|
onStateChange(new int[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecoloredDrawable(Drawable drawable, int i) {
|
|
||||||
this(drawable, new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecoloredDrawable() {
|
|
||||||
this.c = 255;
|
|
||||||
this.d = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(int i) {
|
public void a(int i) {
|
||||||
a(new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
|
a(new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
|
||||||
}
|
}
|
||||||
@@ -129,4 +120,13 @@ public class RecoloredDrawable extends Drawable implements Drawable.Callback {
|
|||||||
public Drawable a() {
|
public Drawable a() {
|
||||||
return this.drawable;
|
return this.drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RecoloredDrawable(Drawable drawable, int i) {
|
||||||
|
this(drawable, new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecoloredDrawable() {
|
||||||
|
this.c = 255;
|
||||||
|
this.d = 255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,13 +21,6 @@ public final class VerifyInfoHelper {
|
|||||||
public static final VerifyInfoHelper h;
|
public static final VerifyInfoHelper h;
|
||||||
public static int devIcon = R.drawable.ic_favorite_16;
|
public static int devIcon = R.drawable.ic_favorite_16;
|
||||||
|
|
||||||
static {
|
|
||||||
h = new VerifyInfoHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
private VerifyInfoHelper() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Integer enumSwitchMapping(VerifyInfoHelper.ColorTheme color) {
|
public static Integer enumSwitchMapping(VerifyInfoHelper.ColorTheme color) {
|
||||||
return switch (color) {
|
return switch (color) {
|
||||||
case normal -> 1;
|
case normal -> 1;
|
||||||
@@ -37,47 +30,18 @@ public final class VerifyInfoHelper {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Drawable b(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, boolean dev, Context context, ColorTheme colorTheme, int i, Object obj) {
|
public enum ColorTheme {
|
||||||
if ((i & 8) != 0) {
|
white,
|
||||||
colorTheme = ColorTheme.normal;
|
normal,
|
||||||
}
|
light,
|
||||||
return verifyInfoHelper.b(z, z2, dev, context, colorTheme);
|
ultraLight
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Drawable b(VerifyInfoHelper verifyInfoHelper, VerifyInfo verifyInfo, Context context, ColorTheme colorTheme, int i, Object obj) {
|
static {
|
||||||
return b(verifyInfoHelper, verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme, i, obj);
|
h = new VerifyInfoHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Drawable b(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, Context context, ColorTheme colorTheme, int i, Object obj) {
|
private VerifyInfoHelper() {
|
||||||
return b(verifyInfoHelper, z, z2, false, context, colorTheme, i, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Drawable a(VerifyInfoHelper verifyInfoHelper, VerifyInfo verifyInfo, Context context, ColorTheme colorTheme, int i, Object obj) {
|
|
||||||
if ((i & 4) != 0) {
|
|
||||||
colorTheme = ColorTheme.normal;
|
|
||||||
}
|
|
||||||
return verifyInfoHelper.b(verifyInfo, context, colorTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Drawable a(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, boolean dev, Context context, ColorTheme colorTheme, int i, Object obj) {
|
|
||||||
if ((i & 8) != 0) {
|
|
||||||
colorTheme = ColorTheme.normal;
|
|
||||||
}
|
|
||||||
return verifyInfoHelper.a(z, z2, dev, context, colorTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Drawable a(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, Context context, ColorTheme colorTheme, int i, Object obj) {
|
|
||||||
return a(verifyInfoHelper, z, z2, false, context, colorTheme, i, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void a(VerifyInfoHelper verifyInfoHelper, TextView textView, VerifyInfo verifyInfo, boolean z, ColorTheme colorTheme, int i, Object obj) {
|
|
||||||
if ((i & 4) != 0) {
|
|
||||||
z = false;
|
|
||||||
}
|
|
||||||
if ((i & 8) != 0) {
|
|
||||||
colorTheme = ColorTheme.normal;
|
|
||||||
}
|
|
||||||
verifyInfoHelper.a(textView, verifyInfo, z, colorTheme);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int a() {
|
private int a() {
|
||||||
@@ -125,6 +89,7 @@ public final class VerifyInfoHelper {
|
|||||||
return b(this, z, z2, false, context, null, 8, null);
|
return b(this, z, z2, false, context, null, 8, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int b(ColorTheme colorTheme) {
|
public int b(ColorTheme colorTheme) {
|
||||||
return a(colorTheme);
|
return a(colorTheme);
|
||||||
}
|
}
|
||||||
@@ -139,6 +104,7 @@ public final class VerifyInfoHelper {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Drawable b(Context context, VerifyInfo verifyInfo) {
|
public Drawable b(Context context, VerifyInfo verifyInfo) {
|
||||||
if (verifyInfo.dev()) {
|
if (verifyInfo.dev()) {
|
||||||
return new RecoloredDrawable(ContextCompat.getDrawable(context, devIcon), c(ColorTheme.normal));
|
return new RecoloredDrawable(ContextCompat.getDrawable(context, devIcon), c(ColorTheme.normal));
|
||||||
@@ -157,6 +123,21 @@ public final class VerifyInfoHelper {
|
|||||||
return b(verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme);
|
return b(verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Drawable b(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, boolean dev, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
if ((i & 8) != 0) {
|
||||||
|
colorTheme = ColorTheme.normal;
|
||||||
|
}
|
||||||
|
return verifyInfoHelper.b(z, z2, dev, context, colorTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable b(VerifyInfoHelper verifyInfoHelper, VerifyInfo verifyInfo, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
return b(verifyInfoHelper, verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme, i, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable b(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
return b(verifyInfoHelper, z, z2, false, context, colorTheme, i, obj);
|
||||||
|
}
|
||||||
|
|
||||||
public Drawable a(Context context, VerifyInfo verifyInfo) {
|
public Drawable a(Context context, VerifyInfo verifyInfo) {
|
||||||
if (verifyInfo.dev()) {
|
if (verifyInfo.dev()) {
|
||||||
return new RecoloredDrawable(ContextCompat.getDrawable(context, devIcon), c(ColorTheme.normal));
|
return new RecoloredDrawable(ContextCompat.getDrawable(context, devIcon), c(ColorTheme.normal));
|
||||||
@@ -200,10 +181,28 @@ public final class VerifyInfoHelper {
|
|||||||
return new RecoloredDrawable(ContextCompat.getDrawable(context, i), c2);
|
return new RecoloredDrawable(ContextCompat.getDrawable(context, i), c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Drawable a(VerifyInfoHelper verifyInfoHelper, VerifyInfo verifyInfo, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
if ((i & 4) != 0) {
|
||||||
|
colorTheme = ColorTheme.normal;
|
||||||
|
}
|
||||||
|
return verifyInfoHelper.b(verifyInfo, context, colorTheme);
|
||||||
|
}
|
||||||
|
|
||||||
public Drawable a(VerifyInfo verifyInfo, Context context, ColorTheme colorTheme) {
|
public Drawable a(VerifyInfo verifyInfo, Context context, ColorTheme colorTheme) {
|
||||||
return a(verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme);
|
return a(verifyInfo.u1(), verifyInfo.t1(), verifyInfo.dev(), context, colorTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Drawable a(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, boolean dev, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
if ((i & 8) != 0) {
|
||||||
|
colorTheme = ColorTheme.normal;
|
||||||
|
}
|
||||||
|
return verifyInfoHelper.a(z, z2, dev, context, colorTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable a(VerifyInfoHelper verifyInfoHelper, boolean z, boolean z2, Context context, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
return a(verifyInfoHelper, z, z2, false, context, colorTheme, i, obj);
|
||||||
|
}
|
||||||
|
|
||||||
public Drawable a(boolean z, boolean z2, Context context, ColorTheme colorTheme) {
|
public Drawable a(boolean z, boolean z2, Context context, ColorTheme colorTheme) {
|
||||||
return a(z, z2, false, context, colorTheme);
|
return a(z, z2, false, context, colorTheme);
|
||||||
}
|
}
|
||||||
@@ -231,6 +230,16 @@ public final class VerifyInfoHelper {
|
|||||||
return new RecoloredDrawable(ContextCompat.getDrawable(context, i), c2);
|
return new RecoloredDrawable(ContextCompat.getDrawable(context, i), c2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void a(VerifyInfoHelper verifyInfoHelper, TextView textView, VerifyInfo verifyInfo, boolean z, ColorTheme colorTheme, int i, Object obj) {
|
||||||
|
if ((i & 4) != 0) {
|
||||||
|
z = false;
|
||||||
|
}
|
||||||
|
if ((i & 8) != 0) {
|
||||||
|
colorTheme = ColorTheme.normal;
|
||||||
|
}
|
||||||
|
verifyInfoHelper.a(textView, verifyInfo, z, colorTheme);
|
||||||
|
}
|
||||||
|
|
||||||
public void a(TextView textView, VerifyInfo verifyInfo, boolean z, ColorTheme colorTheme) {
|
public void a(TextView textView, VerifyInfo verifyInfo, boolean z, ColorTheme colorTheme) {
|
||||||
Drawable drawable;
|
Drawable drawable;
|
||||||
Context context = textView.getContext();
|
Context context = textView.getContext();
|
||||||
@@ -258,11 +267,4 @@ public final class VerifyInfoHelper {
|
|||||||
}
|
}
|
||||||
ViewExtKt.p(imageView);
|
ViewExtKt.p(imageView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ColorTheme {
|
|
||||||
white,
|
|
||||||
normal,
|
|
||||||
light,
|
|
||||||
ultraLight
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+25
-25
@@ -31,6 +31,30 @@ public final class DialogGetMentionSuggestionCmd extends BaseImEngineCmd<Suggest
|
|||||||
private final boolean e;
|
private final boolean e;
|
||||||
private final Object f;
|
private final Object f;
|
||||||
|
|
||||||
|
public static final class a<T> implements Comparator<Member> {
|
||||||
|
List a;
|
||||||
|
|
||||||
|
a(List list) {
|
||||||
|
this.a = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // java.util.Comparator
|
||||||
|
/* renamed from: a */
|
||||||
|
public int compare(Member member, Member member2) {
|
||||||
|
int indexOf = this.a.indexOf(member);
|
||||||
|
int indexOf2 = this.a.indexOf(member2);
|
||||||
|
int i = indexOf;
|
||||||
|
if (indexOf < 0) {
|
||||||
|
i = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
int i2 = indexOf2;
|
||||||
|
if (indexOf2 < 0) {
|
||||||
|
i2 = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return Intrinsics.a(i, i2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public DialogGetMentionSuggestionCmd(int i, String str, Source source, boolean z, Object obj) {
|
public DialogGetMentionSuggestionCmd(int i, String str, Source source, boolean z, Object obj) {
|
||||||
this.f2365b = i;
|
this.f2365b = i;
|
||||||
this.c = str;
|
this.c = str;
|
||||||
@@ -126,7 +150,7 @@ public final class DialogGetMentionSuggestionCmd extends BaseImEngineCmd<Suggest
|
|||||||
Collection<Member> a2;
|
Collection<Member> a2;
|
||||||
Source source;
|
Source source;
|
||||||
if (ImDialogsUtils.d(this.f2365b) != PeerType.CHAT) {
|
if (ImDialogsUtils.d(this.f2365b) != PeerType.CHAT) {
|
||||||
return new Suggestion(null, null);
|
return new Suggestion((EntityValue) null, (ProfilesInfo) null);
|
||||||
}
|
}
|
||||||
Member Z = imEnvironment.Z();
|
Member Z = imEnvironment.Z();
|
||||||
int d = imEnvironment.a0().n().d();
|
int d = imEnvironment.a0().n().d();
|
||||||
@@ -211,28 +235,4 @@ public final class DialogGetMentionSuggestionCmd extends BaseImEngineCmd<Suggest
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "DialogGetMentionSuggestionCmd(dialogId=" + this.f2365b + ", query=" + this.c + ", source=" + this.d + ", isAwaitNetwork=" + this.e + ", changerTag=" + this.f + ")";
|
return "DialogGetMentionSuggestionCmd(dialogId=" + this.f2365b + ", query=" + this.c + ", source=" + this.d + ", isAwaitNetwork=" + this.e + ", changerTag=" + this.f + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class a<T> implements Comparator<Member> {
|
|
||||||
List a;
|
|
||||||
|
|
||||||
a(List list) {
|
|
||||||
this.a = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // java.util.Comparator
|
|
||||||
/* renamed from: a */
|
|
||||||
public int compare(Member member, Member member2) {
|
|
||||||
int indexOf = this.a.indexOf(member);
|
|
||||||
int indexOf2 = this.a.indexOf(member2);
|
|
||||||
int i = indexOf;
|
|
||||||
if (indexOf < 0) {
|
|
||||||
i = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
int i2 = indexOf2;
|
|
||||||
if (indexOf2 < 0) {
|
|
||||||
i2 = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
return Intrinsics.a(i, i2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ import java.util.List;
|
|||||||
public final class DialogActionsHelper {
|
public final class DialogActionsHelper {
|
||||||
public static final DialogActionsHelper a = new DialogActionsHelper();
|
public static final DialogActionsHelper a = new DialogActionsHelper();
|
||||||
|
|
||||||
public List<DialogAction> a(ImConfig imConfig, Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
|
public final List<DialogAction> a(ImConfig imConfig, Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
|
||||||
if (dialog != null && profilesSimpleInfo != null) {
|
if (dialog != null && profilesSimpleInfo != null) {
|
||||||
long b2 = TimeProvider.f.b();
|
long b2 = TimeProvider.f.b();
|
||||||
ChatSettings z1 = dialog.z1();
|
ChatSettings z1 = dialog.z1();
|
||||||
@@ -84,7 +84,7 @@ public final class DialogActionsHelper {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DialogAction> a(Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
|
public final List<DialogAction> a(Dialog dialog, ProfilesSimpleInfo profilesSimpleInfo) {
|
||||||
long b2 = TimeProvider.f.b();
|
long b2 = TimeProvider.f.b();
|
||||||
Profile profile = profilesSimpleInfo.get(dialog.getId());
|
Profile profile = profilesSimpleInfo.get(dialog.getId());
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public final class DialogActionsHelper {
|
|||||||
return arrayList;
|
return arrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean a(Dialog dialog) {
|
public final boolean a(Dialog dialog) {
|
||||||
if (dialog.V1() || !dialog.y1()) {
|
if (dialog.V1() || !dialog.y1()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ import java.util.List;
|
|||||||
public final class MsgActionHelper {
|
public final class MsgActionHelper {
|
||||||
public static final MsgActionHelper a = new MsgActionHelper();
|
public static final MsgActionHelper a = new MsgActionHelper();
|
||||||
|
|
||||||
public List<MsgAction> a(ImEngine imEngine, Dialog dialog, Collection<? extends Msg> collection) {
|
public final List<MsgAction> a(ImEngine imEngine, Dialog dialog, Collection<? extends Msg> collection) {
|
||||||
return a(imEngine != null ? imEngine.g() : null, dialog, collection);
|
return a(imEngine != null ? imEngine.g() : null, dialog, collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MsgAction> a(ImConfig imConfig, Dialog dialog, Collection<? extends Msg> collection) {
|
public final List<MsgAction> a(ImConfig imConfig, Dialog dialog, Collection<? extends Msg> collection) {
|
||||||
if (imConfig != null && dialog != null && collection != null) {
|
if (imConfig != null && dialog != null && collection != null) {
|
||||||
MsgPermissionHelper.a aVar = new MsgPermissionHelper.a(imConfig, dialog, collection);
|
MsgPermissionHelper.a aVar = new MsgPermissionHelper.a(imConfig, dialog, collection);
|
||||||
ArrayList<MsgAction> arrayList = new ArrayList<>();
|
ArrayList<MsgAction> arrayList = new ArrayList<>();
|
||||||
|
|||||||
+137
-29
@@ -74,7 +74,11 @@ public class VhBindArgs {
|
|||||||
|
|
||||||
private boolean L() {
|
private boolean L() {
|
||||||
boolean var1;
|
boolean var1;
|
||||||
var1 = this.K() > this.m || TimeProvider.f.c();
|
if (this.K() <= this.m && !TimeProvider.f.c()) {
|
||||||
|
var1 = false;
|
||||||
|
} else {
|
||||||
|
var1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
return var1;
|
return var1;
|
||||||
}
|
}
|
||||||
@@ -105,7 +109,11 @@ public class VhBindArgs {
|
|||||||
public boolean B() {
|
public boolean B() {
|
||||||
AdapterEntry var1 = this.a;
|
AdapterEntry var1 = this.a;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.c();
|
if (var1 != null && var1.c()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -113,7 +121,11 @@ public class VhBindArgs {
|
|||||||
public boolean C() {
|
public boolean C() {
|
||||||
AdapterEntry var1 = this.a;
|
AdapterEntry var1 = this.a;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.c() && this.a.c.getLocalId() == this.b.c.getLocalId();
|
if (var1 != null && var1.c() && this.a.c.getLocalId() == this.b.c.getLocalId()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -121,7 +133,11 @@ public class VhBindArgs {
|
|||||||
public boolean D() {
|
public boolean D() {
|
||||||
AdapterEntry var1 = this.a;
|
AdapterEntry var1 = this.a;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a == 83;
|
if (var1 != null && var1.a == 83) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -129,7 +145,11 @@ public class VhBindArgs {
|
|||||||
public boolean E() {
|
public boolean E() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).i2();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).i2()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -137,7 +157,11 @@ public class VhBindArgs {
|
|||||||
public boolean F() {
|
public boolean F() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).F0();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).F0()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -145,7 +169,11 @@ public class VhBindArgs {
|
|||||||
public boolean G() {
|
public boolean G() {
|
||||||
AdapterEntry var1 = this.b;
|
AdapterEntry var1 = this.b;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a == 51;
|
if (var1 != null && var1.a == 51) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -158,7 +186,11 @@ public class VhBindArgs {
|
|||||||
long var1 = this.K();
|
long var1 = this.K();
|
||||||
long var3 = this.m;
|
long var3 = this.m;
|
||||||
boolean var5;
|
boolean var5;
|
||||||
var5 = var1 > var3 && var1 < var3 * 2L;
|
if (var1 > var3 && var1 < var3 * 2L) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var5;
|
return var5;
|
||||||
}
|
}
|
||||||
@@ -174,10 +206,18 @@ public class VhBindArgs {
|
|||||||
MsgFromUser var7 = (MsgFromUser) var1;
|
MsgFromUser var7 = (MsgFromUser) var1;
|
||||||
Msg var3 = this.a.c;
|
Msg var3 = this.a.c;
|
||||||
boolean var4;
|
boolean var4;
|
||||||
var4 = var3 != null && var3.d() == MsgSyncState.DONE;
|
if (var3 != null && var3.d() == MsgSyncState.DONE) {
|
||||||
|
var4 = true;
|
||||||
|
} else {
|
||||||
|
var4 = false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean var5;
|
boolean var5;
|
||||||
var5 = this.o == ImBgSyncState.CONNECTED;
|
if (this.o == ImBgSyncState.CONNECTED) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean var6 = var2;
|
boolean var6 = var2;
|
||||||
if (!var7.H0()) {
|
if (!var7.H0()) {
|
||||||
@@ -202,7 +242,7 @@ public class VhBindArgs {
|
|||||||
byte var2 = 0;
|
byte var2 = 0;
|
||||||
int var3 = var2;
|
int var3 = var2;
|
||||||
if (var1) {
|
if (var1) {
|
||||||
AttachAudioMsg var4 = ((MsgFromUser) this.b.c).c(AttachAudioMsg.class, false);
|
AttachAudioMsg var4 = (AttachAudioMsg) ((MsgFromUser) this.b.c).c(AttachAudioMsg.class, false);
|
||||||
var3 = var2;
|
var3 = var2;
|
||||||
if (var4 != null) {
|
if (var4 != null) {
|
||||||
var3 = var4.f();
|
var3 = var4.f();
|
||||||
@@ -227,7 +267,11 @@ public class VhBindArgs {
|
|||||||
public boolean c() {
|
public boolean c() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).I0();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).I0()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -235,14 +279,22 @@ public class VhBindArgs {
|
|||||||
public boolean d() {
|
public boolean d() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof WithUserContent && ((WithUserContent) var1).D0() != null;
|
if (var1 instanceof WithUserContent && ((WithUserContent) var1).D0() != null) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean e() {
|
public boolean e() {
|
||||||
boolean var1;
|
boolean var1;
|
||||||
var1 = this.b.i > 0;
|
if (this.b.i > 0) {
|
||||||
|
var1 = true;
|
||||||
|
} else {
|
||||||
|
var1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var1;
|
return var1;
|
||||||
}
|
}
|
||||||
@@ -265,7 +317,11 @@ public class VhBindArgs {
|
|||||||
public boolean g() {
|
public boolean g() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).G0();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).G0()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -273,21 +329,33 @@ public class VhBindArgs {
|
|||||||
public boolean h() {
|
public boolean h() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).Z1();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).Z1()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean i() {
|
public boolean i() {
|
||||||
boolean var1;
|
boolean var1;
|
||||||
var1 = this.j && !this.u() && !this.l();
|
if (this.j && !this.u() && !this.l()) {
|
||||||
|
var1 = true;
|
||||||
|
} else {
|
||||||
|
var1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var1;
|
return var1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean j() {
|
public boolean j() {
|
||||||
boolean var1;
|
boolean var1;
|
||||||
var1 = this.j && !this.u() && (!this.B() || !this.A() || this.a.c.H1());
|
if (!this.j || this.u() || this.B() && this.A() && !this.a.c.H1()) {
|
||||||
|
var1 = false;
|
||||||
|
} else {
|
||||||
|
var1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
return var1;
|
return var1;
|
||||||
}
|
}
|
||||||
@@ -295,7 +363,11 @@ public class VhBindArgs {
|
|||||||
public boolean k() {
|
public boolean k() {
|
||||||
Dialog var1 = this.e;
|
Dialog var1 = this.e;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.U1();
|
if (var1 != null && var1.U1()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -303,7 +375,11 @@ public class VhBindArgs {
|
|||||||
public boolean l() {
|
public boolean l() {
|
||||||
Dialog var1 = this.e;
|
Dialog var1 = this.e;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.V1();
|
if (var1 != null && var1.V1()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -311,7 +387,11 @@ public class VhBindArgs {
|
|||||||
public boolean m() {
|
public boolean m() {
|
||||||
Dialog var1 = this.e;
|
Dialog var1 = this.e;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.W1();
|
if (var1 != null && var1.W1()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -324,7 +404,11 @@ public class VhBindArgs {
|
|||||||
public boolean o() {
|
public boolean o() {
|
||||||
AdapterEntry var1 = this.b;
|
AdapterEntry var1 = this.b;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a == 101;
|
if (var1 != null && var1.a == 101) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -332,7 +416,11 @@ public class VhBindArgs {
|
|||||||
public boolean p() {
|
public boolean p() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.G1();
|
if (var1 != null && var1.G1()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -355,7 +443,11 @@ public class VhBindArgs {
|
|||||||
public boolean r() {
|
public boolean r() {
|
||||||
Msg var1 = this.b.c;
|
Msg var1 = this.b.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 instanceof MsgFromUser && ((MsgFromUser) var1).e2();
|
if (var1 instanceof MsgFromUser && ((MsgFromUser) var1).e2()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -396,19 +488,27 @@ public class VhBindArgs {
|
|||||||
public boolean v() {
|
public boolean v() {
|
||||||
IntSet var1 = this.r;
|
IntSet var1 = this.r;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a(this.b.c.getLocalId());
|
if (var1 != null && var1.a(this.b.c.getLocalId())) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean w() {
|
public boolean w() {
|
||||||
return !this.z();
|
return this.z() ^ true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean x() {
|
public boolean x() {
|
||||||
AdapterEntry var1 = this.c;
|
AdapterEntry var1 = this.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a == 100;
|
if (var1 != null && var1.a == 100) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -416,7 +516,11 @@ public class VhBindArgs {
|
|||||||
public boolean y() {
|
public boolean y() {
|
||||||
AdapterEntry var1 = this.c;
|
AdapterEntry var1 = this.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.a == 96;
|
if (var1 != null && var1.a == 96) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -424,7 +528,11 @@ public class VhBindArgs {
|
|||||||
public boolean z() {
|
public boolean z() {
|
||||||
AdapterEntry var1 = this.c;
|
AdapterEntry var1 = this.c;
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 != null && var1.c() && this.c.c.getLocalId() == this.b.c.getLocalId();
|
if (var1 != null && var1.c() && this.c.c.getLocalId() == this.b.c.getLocalId()) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -46,7 +46,7 @@ public final class MsgPartCallHolder extends MsgPartHolderBase<AttachCall> {
|
|||||||
this.G = new StringBuilder();
|
this.G = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void a(Msg var1) {
|
private final void a(Msg var1) {
|
||||||
this.G.setLength(0);
|
this.G.setLength(0);
|
||||||
MsgTimeFormatter var2 = this.F;
|
MsgTimeFormatter var2 = this.F;
|
||||||
Context var3 = this.C;
|
Context var3 = this.C;
|
||||||
@@ -55,7 +55,7 @@ public final class MsgPartCallHolder extends MsgPartHolderBase<AttachCall> {
|
|||||||
this.H.setTimeText(this.G);
|
this.H.setTimeText(this.G);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void a(boolean var1) {
|
private final void a(boolean var1) {
|
||||||
MsgPartIconTwoRowView var2 = this.H;
|
MsgPartIconTwoRowView var2 = this.H;
|
||||||
int var3;
|
int var3;
|
||||||
if (var1) {
|
if (var1) {
|
||||||
@@ -67,7 +67,7 @@ public final class MsgPartCallHolder extends MsgPartHolderBase<AttachCall> {
|
|||||||
var2.setIcon(var3);
|
var2.setIcon(var3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void a(boolean var1, AttachCall var2) {
|
private final void a(boolean var1, AttachCall var2) {
|
||||||
boolean var3 = !var1;
|
boolean var3 = !var1;
|
||||||
if (var2.h() == CallState.ERROR) {
|
if (var2.h() == CallState.ERROR) {
|
||||||
this.H.setSubtitleText(m.vkim_msg_list_call_error);
|
this.H.setSubtitleText(m.vkim_msg_list_call_error);
|
||||||
|
|||||||
+159
-104
@@ -49,7 +49,6 @@ import com.vk.im.ui.views.msg.bubble.MsgBubbleView;
|
|||||||
import com.vk.im.ui.views.sticker.StickerAnimationState;
|
import com.vk.im.ui.views.sticker.StickerAnimationState;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.b.Functions;
|
import kotlin.jvm.b.Functions;
|
||||||
import ru.vtosters.lite.themes.ThemesHacks;
|
|
||||||
|
|
||||||
public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
||||||
public static final int h0 = Screen.a(124);
|
public static final int h0 = Screen.a(124);
|
||||||
@@ -57,6 +56,46 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
private static final int[][] j0;
|
private static final int[][] j0;
|
||||||
private static final int[][] k0;
|
private static final int[][] k0;
|
||||||
private static final int[][] l0;
|
private static final int[][] l0;
|
||||||
|
private final MsgPartHolderBase B;
|
||||||
|
private final MsgPartHolderBindArgs C = new MsgPartHolderBindArgs();
|
||||||
|
private final Rect D = new Rect();
|
||||||
|
private final Rect E = new Rect();
|
||||||
|
private final Rect F = new Rect();
|
||||||
|
private final Rect G = new Rect();
|
||||||
|
private ColorDrawable H;
|
||||||
|
private final int I;
|
||||||
|
private final int J;
|
||||||
|
private final int K;
|
||||||
|
private final int L;
|
||||||
|
private final int M;
|
||||||
|
private Drawable N;
|
||||||
|
private Drawable O;
|
||||||
|
private BombView P;
|
||||||
|
private BombView.d Q;
|
||||||
|
private int R;
|
||||||
|
private int S;
|
||||||
|
private final DisplayNameFormatter T = new DisplayNameFormatter();
|
||||||
|
private final StringBuilder U = new StringBuilder();
|
||||||
|
private final StringBuilder V = new StringBuilder();
|
||||||
|
private final String W;
|
||||||
|
private final String X;
|
||||||
|
private final String Y;
|
||||||
|
private boolean Z;
|
||||||
|
private BubbleColors a0;
|
||||||
|
private final FluidHorizontalLayout b;
|
||||||
|
@ColorInt
|
||||||
|
private int b0;
|
||||||
|
private final AvatarView c;
|
||||||
|
private MsgListAdapterCallback c0;
|
||||||
|
private final Space d;
|
||||||
|
private Msg d0;
|
||||||
|
private final MsgBubbleView e;
|
||||||
|
private Dialog e0;
|
||||||
|
private final MsgStatusView f;
|
||||||
|
private int f0;
|
||||||
|
private final ImageView g;
|
||||||
|
private h g0 = new h();
|
||||||
|
private final Space h;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
int[] var0 = new int[]{0, 0, 0, 0};
|
int[] var0 = new int[]{0, 0, 0, 0};
|
||||||
@@ -80,61 +119,20 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
l0 = new int[][]{var0, {4, 4, 0, 0, 0, 2, 8, 0}, var1, var2, {4, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 0, 0, 2, 8, 0}, {4, 4, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 0, 0, 0, 0, 0}};
|
l0 = new int[][]{var0, {4, 4, 0, 0, 0, 2, 8, 0}, var1, var2, {4, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 0, 0, 2, 8, 0}, {4, 4, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 0, 0, 0, 0, 0}};
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MsgPartHolderBase B;
|
|
||||||
private final MsgPartHolderBindArgs C = new MsgPartHolderBindArgs();
|
|
||||||
private final Rect D = new Rect();
|
|
||||||
private final Rect E = new Rect();
|
|
||||||
private final Rect F = new Rect();
|
|
||||||
private final Rect G = new Rect();
|
|
||||||
private final int I;
|
|
||||||
private final int J;
|
|
||||||
private final int K;
|
|
||||||
private final int L;
|
|
||||||
private final int M;
|
|
||||||
private final DisplayNameFormatter T = new DisplayNameFormatter();
|
|
||||||
private final StringBuilder U = new StringBuilder();
|
|
||||||
private final StringBuilder V = new StringBuilder();
|
|
||||||
private final String W;
|
|
||||||
private final String X;
|
|
||||||
private final String Y;
|
|
||||||
private final FluidHorizontalLayout b;
|
|
||||||
private final AvatarView c;
|
|
||||||
private final Space d;
|
|
||||||
private final MsgBubbleView e;
|
|
||||||
private final MsgStatusView f;
|
|
||||||
private final ImageView g;
|
|
||||||
private final Space h;
|
|
||||||
private final ColorDrawable H;
|
|
||||||
private Drawable N;
|
|
||||||
private Drawable O;
|
|
||||||
private final BombView P;
|
|
||||||
private final BombView.d Q;
|
|
||||||
private final int R;
|
|
||||||
private final int S;
|
|
||||||
private boolean Z;
|
|
||||||
private BubbleColors a0;
|
|
||||||
@ColorInt
|
|
||||||
private int b0;
|
|
||||||
private MsgListAdapterCallback c0;
|
|
||||||
private Msg d0;
|
|
||||||
private Dialog e0;
|
|
||||||
private int f0;
|
|
||||||
private final h g0 = new h();
|
|
||||||
|
|
||||||
public VhMsg(View itemView, MsgPartHolderBase vhBase) {
|
public VhMsg(View itemView, MsgPartHolderBase vhBase) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
Context context = itemView.getContext();
|
Context context = itemView.getContext();
|
||||||
LayoutInflater var4 = LayoutInflater.from(context);
|
LayoutInflater var4 = LayoutInflater.from(context);
|
||||||
this.b = (FluidHorizontalLayout) itemView;
|
this.b = (FluidHorizontalLayout) itemView;
|
||||||
this.c = itemView.findViewById(com.vk.im.ui.h.avatar);
|
this.c = (AvatarView) itemView.findViewById(com.vk.im.ui.h.avatar);
|
||||||
this.d = itemView.findViewById(com.vk.im.ui.h.avatar_space);
|
this.d = (Space) itemView.findViewById(com.vk.im.ui.h.avatar_space);
|
||||||
this.e = itemView.findViewById(com.vk.im.ui.h.bubble);
|
this.e = (MsgBubbleView) itemView.findViewById(com.vk.im.ui.h.bubble);
|
||||||
this.f = itemView.findViewById(com.vk.im.ui.h.status);
|
this.f = (MsgStatusView) itemView.findViewById(com.vk.im.ui.h.status);
|
||||||
this.g = itemView.findViewById(com.vk.im.ui.h.vkim_channel_share);
|
this.g = (ImageView) itemView.findViewById(com.vk.im.ui.h.vkim_channel_share);
|
||||||
this.h = itemView.findViewById(com.vk.im.ui.h.status_space);
|
this.h = (Space) itemView.findViewById(com.vk.im.ui.h.status_space);
|
||||||
this.P = itemView.findViewById(com.vk.im.ui.h.bomb);
|
this.P = (BombView) itemView.findViewById(com.vk.im.ui.h.bomb);
|
||||||
this.B = vhBase;
|
this.B = vhBase;
|
||||||
this.H = new ColorDrawable(ThemesHacks.getColors(context, com.vk.im.ui.d.msg_search_selection));
|
this.H = new ColorDrawable(context.getResources().getColor(com.vk.im.ui.d.msg_search_selection));
|
||||||
this.I = ContextExtKt.b(context, com.vk.im.ui.e.msg_bubble_max_width);
|
this.I = ContextExtKt.b(context, com.vk.im.ui.e.msg_bubble_max_width);
|
||||||
this.J = ContextExtKt.b(context, com.vk.im.ui.e.msg_reply_max_width);
|
this.J = ContextExtKt.b(context, com.vk.im.ui.e.msg_reply_max_width);
|
||||||
this.K = ContextExtKt.b(context, com.vk.im.ui.e.msg_sticker_max_width);
|
this.K = ContextExtKt.b(context, com.vk.im.ui.e.msg_sticker_max_width);
|
||||||
@@ -151,39 +149,49 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
var6.right = var7.rightMargin;
|
var6.right = var7.rightMargin;
|
||||||
var6.top = var7.topMargin;
|
var6.top = var7.topMargin;
|
||||||
var6.bottom = var7.bottomMargin;
|
var6.bottom = var7.bottomMargin;
|
||||||
ViewExtKt.b(this.c, var1 -> {
|
ViewExtKt.b(this.c, new View.OnClickListener() {
|
||||||
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
public void onClick(View var1) {
|
||||||
VhMsg.this.c0.a(VhMsg.this.d0.getFrom());
|
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
||||||
}
|
VhMsg.this.c0.a(VhMsg.this.d0.getFrom());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.c.setOnLongClickListener(var1 -> {
|
this.c.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
public boolean onLongClick(View var1) {
|
||||||
VhMsg.this.c0.a(VhMsg.this.d0.getFrom());
|
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
||||||
|
VhMsg.this.c0.a(VhMsg.this.d0.getFrom());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ViewExtKt.b(this.g, new View.OnClickListener() {
|
||||||
|
public void onClick(View var1) {
|
||||||
|
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
||||||
|
VhMsg.this.c0.a(VhMsg.this.d0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ViewExtKt.b(this.itemView, new View.OnClickListener() {
|
||||||
|
public void onClick(View var1) {
|
||||||
|
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
||||||
|
VhMsg.this.c0.a(VhMsg.this.d0.getLocalId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
public boolean onLongClick(View var1) {
|
||||||
|
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
||||||
|
VhMsg.this.c0.b(VhMsg.this.d0.getLocalId());
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ViewExtKt.b(this.g, var1 -> {
|
|
||||||
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
|
||||||
VhMsg.this.c0.a(VhMsg.this.d0);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
ViewExtKt.b(this.itemView, (View.OnClickListener) var1 -> {
|
|
||||||
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
|
||||||
VhMsg.this.c0.a(VhMsg.this.d0.getLocalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
this.itemView.setOnLongClickListener((View.OnLongClickListener) var1 -> {
|
|
||||||
if (VhMsg.this.c0 != null && VhMsg.this.d0 != null) {
|
|
||||||
VhMsg.this.c0.b(VhMsg.this.d0.getLocalId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
this.R = ContextExtKt.i(context, com.vk.im.ui.c.im_msg_part_corner_radius_small);
|
this.R = ContextExtKt.i(context, com.vk.im.ui.c.im_msg_part_corner_radius_small);
|
||||||
this.S = ContextExtKt.i(context, com.vk.im.ui.c.im_msg_part_corner_radius_big);
|
this.S = ContextExtKt.i(context, com.vk.im.ui.c.im_msg_part_corner_radius_big);
|
||||||
this.c0 = null;
|
this.c0 = null;
|
||||||
@@ -195,10 +203,6 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VhMsg a(LayoutInflater var0, ViewGroup var1, MsgPartHolderBase var2) {
|
|
||||||
return new VhMsg(var0.inflate(j.vkim_msg_list_item_msg_from_user, var1, false), var2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int a(Style var1, Style var2, boolean var3) {
|
private int a(Style var1, Style var2, boolean var3) {
|
||||||
int var4;
|
int var4;
|
||||||
if (var3) {
|
if (var3) {
|
||||||
@@ -279,15 +283,20 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
case 103:
|
case 103:
|
||||||
return VhMsg.Style.EMPTY;
|
return VhMsg.Style.EMPTY;
|
||||||
default:
|
default:
|
||||||
String var3 = "Unknown viewType: " +
|
StringBuilder var3 = new StringBuilder();
|
||||||
var1;
|
var3.append("Unknown viewType: ");
|
||||||
throw new IllegalArgumentException(var3);
|
var3.append(var1);
|
||||||
|
throw new IllegalArgumentException(var3.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return VhMsg.Style.TWO_LINE;
|
return VhMsg.Style.TWO_LINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VhMsg a(LayoutInflater var0, ViewGroup var1, MsgPartHolderBase var2) {
|
||||||
|
return new VhMsg(var0.inflate(j.vkim_msg_list_item_msg_from_user, var1, false), var2);
|
||||||
|
}
|
||||||
|
|
||||||
private void a(VhBindArgs args, Rect rect) {
|
private void a(VhBindArgs args, Rect rect) {
|
||||||
Style var3 = this.a(args.b);
|
Style var3 = this.a(args.b);
|
||||||
boolean var4 = args.b.f();
|
boolean var4 = args.b.f();
|
||||||
@@ -402,7 +411,11 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
Msg var10 = var5.c;
|
Msg var10 = var5.c;
|
||||||
var9 = var6;
|
var9 = var6;
|
||||||
if (var10 != null) {
|
if (var10 != null) {
|
||||||
var9 = var10.v1() == args.g.t1();
|
if (var10.v1() == args.g.t1()) {
|
||||||
|
var9 = true;
|
||||||
|
} else {
|
||||||
|
var9 = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (var10.O1()) {
|
if (var10.O1()) {
|
||||||
int var12 = var10.d().ordinal();
|
int var12 = var10.d().ordinal();
|
||||||
@@ -420,7 +433,11 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
b = args.I();
|
b = args.I();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var6 = var10.C1() <= args.i;
|
if (var10.C1() <= args.i) {
|
||||||
|
var6 = true;
|
||||||
|
} else {
|
||||||
|
var6 = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!var6 && !var9) {
|
if (!var6 && !var9) {
|
||||||
var11 = MsgStatus.UNREAD;
|
var11 = MsgStatus.UNREAD;
|
||||||
@@ -558,14 +575,18 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
partArgs.o = args.n;
|
partArgs.o = args.n;
|
||||||
partArgs.s = args.v();
|
partArgs.s = args.v();
|
||||||
partArgs.t = args.q;
|
partArgs.t = args.q;
|
||||||
partArgs.u = !var4.b();
|
partArgs.u = var4.b() ^ true;
|
||||||
boolean var5;
|
boolean var5;
|
||||||
var5 = var3.i > 0;
|
if (var3.i > 0) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
partArgs.v = var5;
|
partArgs.v = var5;
|
||||||
partArgs.w = args.k;
|
partArgs.w = args.k;
|
||||||
partArgs.x = args.l;
|
partArgs.x = args.l;
|
||||||
partArgs.y = !this.Z;
|
partArgs.y = this.Z ^ true;
|
||||||
partArgs.z = args.s;
|
partArgs.z = args.s;
|
||||||
partArgs.A = args.t;
|
partArgs.A = args.t;
|
||||||
partArgs.l = this.b0;
|
partArgs.l = this.b0;
|
||||||
@@ -650,7 +671,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
private void f(VhBindArgs args) {
|
private void f(VhBindArgs args) {
|
||||||
Msg var2 = args.b.c;
|
Msg var2 = args.b.c;
|
||||||
if (var2 != null) {
|
if (var2 != null) {
|
||||||
this.P.setStateListener(null);
|
this.P.setStateListener((BombView.d) null);
|
||||||
if (var2.M1() && !var2.E1()) {
|
if (var2.M1() && !var2.E1()) {
|
||||||
this.P.setVisibility(View.GONE);
|
this.P.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
@@ -789,12 +810,24 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
this.e.setFwdPadding(this.D);
|
this.e.setFwdPadding(this.D);
|
||||||
this.e.setContentPadding(this.E);
|
this.e.setContentPadding(this.E);
|
||||||
boolean var7;
|
boolean var7;
|
||||||
var7 = args.b.a == 83;
|
if (args.b.a == 83) {
|
||||||
|
var7 = true;
|
||||||
|
} else {
|
||||||
|
var7 = false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean var5;
|
boolean var5;
|
||||||
var5 = args.a.a == 83;
|
if (args.a.a == 83) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
var5 = !args.F() || !var7 && !var5;
|
if (!args.F() || !var7 && !var5) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
MsgBubbleView var8 = this.e;
|
MsgBubbleView var8 = this.e;
|
||||||
boolean var6 = var4;
|
boolean var6 = var4;
|
||||||
@@ -871,10 +904,18 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
boolean var5;
|
boolean var5;
|
||||||
var5 = var2.a == 50;
|
if (var2.a == 50) {
|
||||||
|
var5 = true;
|
||||||
|
} else {
|
||||||
|
var5 = false;
|
||||||
|
}
|
||||||
|
|
||||||
boolean var6;
|
boolean var6;
|
||||||
var6 = !args.z() && var2.i == 0;
|
if (!args.z() && var2.i == 0) {
|
||||||
|
var6 = true;
|
||||||
|
} else {
|
||||||
|
var6 = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (var5 || var6 || args.y() || args.x()) {
|
if (var5 || var6 || args.y() || args.x()) {
|
||||||
var4 = true;
|
var4 = true;
|
||||||
@@ -893,7 +934,11 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
|
|
||||||
private boolean u(int var1) {
|
private boolean u(int var1) {
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = var1 == 2 || var1 == 3;
|
if (var1 != 2 && var1 != 3) {
|
||||||
|
var2 = false;
|
||||||
|
} else {
|
||||||
|
var2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
return var2;
|
return var2;
|
||||||
}
|
}
|
||||||
@@ -915,7 +960,9 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
Msg var1 = this.d0;
|
Msg var1 = this.d0;
|
||||||
if (var1 != null) {
|
if (var1 != null) {
|
||||||
Dialog var2 = this.e0;
|
Dialog var2 = this.e0;
|
||||||
return var2 != null && MsgPermissionHelper.b.a(var2, var1);
|
if (var2 != null && MsgPermissionHelper.b.a(var2, var1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -950,7 +997,11 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
|
|
||||||
public void a(VhBindArgs args) {
|
public void a(VhBindArgs args) {
|
||||||
boolean var2;
|
boolean var2;
|
||||||
var2 = VKThemeHelper.l() == VKTheme.VKAPP_LIGHT;
|
if (VKThemeHelper.l() == VKTheme.VKAPP_LIGHT) {
|
||||||
|
var2 = true;
|
||||||
|
} else {
|
||||||
|
var2 = false;
|
||||||
|
}
|
||||||
|
|
||||||
this.Z = var2;
|
this.Z = var2;
|
||||||
AdapterEntry var3 = args.b;
|
AdapterEntry var3 = args.b;
|
||||||
@@ -1011,7 +1062,11 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
|
|
||||||
public boolean f0() {
|
public boolean f0() {
|
||||||
boolean var1;
|
boolean var1;
|
||||||
var1 = this.f0 == 100;
|
if (this.f0 == 100) {
|
||||||
|
var1 = true;
|
||||||
|
} else {
|
||||||
|
var1 = false;
|
||||||
|
}
|
||||||
|
|
||||||
return var1;
|
return var1;
|
||||||
}
|
}
|
||||||
@@ -1028,7 +1083,7 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
this.B.c(var1);
|
this.B.c(var1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Style {
|
private static enum Style {
|
||||||
BUTTON(6),
|
BUTTON(6),
|
||||||
EMPTY(7),
|
EMPTY(7),
|
||||||
FWD_SENDER(4),
|
FWD_SENDER(4),
|
||||||
@@ -1040,14 +1095,14 @@ public class VhMsg extends VhBase implements SwipeToReplyItemTouchCallback.b {
|
|||||||
|
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
Style(int var3) {
|
private Style(int var3) {
|
||||||
this.index = var3;
|
this.index = var3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class h implements Functions<Unit> {
|
private class h implements Functions<Unit> {
|
||||||
private final int a;
|
private int a;
|
||||||
private final Runnable b;
|
private Runnable b;
|
||||||
|
|
||||||
private h() {
|
private h() {
|
||||||
this.a = Screen.a(12);
|
this.a = Screen.a(12);
|
||||||
|
|||||||
@@ -26,23 +26,30 @@ import com.vtosters.lite.R;
|
|||||||
|
|
||||||
public final class MentionsController {
|
public final class MentionsController {
|
||||||
private final RichEditText a;
|
private final RichEditText a;
|
||||||
private final ImEngine mImEngine;
|
|
||||||
private final View j;
|
|
||||||
private final b k;
|
|
||||||
private boolean state;
|
private boolean state;
|
||||||
private CoordinatorLayout mCoordinatorLayout;
|
private CoordinatorLayout mCoordinatorLayout;
|
||||||
private VkBottomSheetBehavior<RecyclerView> mBottomSheetBehavior;
|
private VkBottomSheetBehavior<RecyclerView> mBottomSheetBehavior;
|
||||||
private boolean e;
|
private boolean e;
|
||||||
private final DialogMentionComponent mDialogMentionComponent;
|
private DialogMentionComponent mDialogMentionComponent;
|
||||||
private final MentionsManager mMentionsManager;
|
private MentionsManager mMentionsManager;
|
||||||
private final int i;
|
private final ImEngine mImEngine;
|
||||||
|
private int i;
|
||||||
|
private final View j;
|
||||||
|
private final b k;
|
||||||
|
|
||||||
|
public interface b {
|
||||||
|
boolean c();
|
||||||
|
|
||||||
|
boolean d();
|
||||||
|
}
|
||||||
|
|
||||||
public MentionsController(ImEngine imEngine, int i, View view, b bVar) {
|
public MentionsController(ImEngine imEngine, int i, View view, b bVar) {
|
||||||
this.mImEngine = imEngine;
|
this.mImEngine = imEngine;
|
||||||
this.i = i;
|
this.i = i;
|
||||||
this.j = view;
|
this.j = view;
|
||||||
this.k = bVar;
|
this.k = bVar;
|
||||||
this.a = this.j.findViewById(R.id.writebar_edit);
|
this.a = (RichEditText) this.j.findViewById(R.id.writebar_edit);
|
||||||
this.mMentionsManager = new MentionsManager(this.a, new MentionSuggestionsSupplier() {
|
this.mMentionsManager = new MentionsManager(this.a, new MentionSuggestionsSupplier() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,7 +71,7 @@ public final class MentionsController {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void b() {
|
public void b() {
|
||||||
MentionsController.this.mDialogMentionComponent.a(0, null);
|
MentionsController.this.mDialogMentionComponent.a(0, (String) null);
|
||||||
MentionsController.this.g();
|
MentionsController.this.g();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -85,18 +92,18 @@ public final class MentionsController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void g() {
|
public final void g() {
|
||||||
this.e = false;
|
this.e = false;
|
||||||
j();
|
j();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void h() {
|
private final void h() {
|
||||||
if (this.state) {
|
if (this.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
View inflate = ((ViewStub) this.j.findViewById(R.id.mentions_container_stub)).inflate();
|
View inflate = ((ViewStub) this.j.findViewById(R.id.mentions_container_stub)).inflate();
|
||||||
ViewGroup viewGroup = inflate.findViewById(R.id.mentions_container);
|
ViewGroup viewGroup = (ViewGroup) inflate.findViewById(R.id.mentions_container);
|
||||||
viewGroup.addView(this.mDialogMentionComponent.a(viewGroup, null));
|
viewGroup.addView(this.mDialogMentionComponent.a(viewGroup, (Bundle) null));
|
||||||
this.mCoordinatorLayout = inflate.findViewById(R.id.mentions_cl);
|
this.mCoordinatorLayout = inflate.findViewById(R.id.mentions_cl);
|
||||||
this.mBottomSheetBehavior = (VkBottomSheetBehavior<RecyclerView>) ((CoordinatorLayout.LayoutParams) viewGroup.getLayoutParams()).getBehavior();
|
this.mBottomSheetBehavior = (VkBottomSheetBehavior<RecyclerView>) ((CoordinatorLayout.LayoutParams) viewGroup.getLayoutParams()).getBehavior();
|
||||||
this.mBottomSheetBehavior.a(new VkBottomSheetBehavior.b() {
|
this.mBottomSheetBehavior.a(new VkBottomSheetBehavior.b() {
|
||||||
@@ -113,12 +120,12 @@ public final class MentionsController {
|
|||||||
this.state = true;
|
this.state = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void i() {
|
public final void i() {
|
||||||
this.e = true;
|
this.e = true;
|
||||||
j();
|
j();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void j() {
|
public final void j() {
|
||||||
if (this.k.d()) {
|
if (this.k.d()) {
|
||||||
Context context = this.j.getContext();
|
Context context = this.j.getContext();
|
||||||
Activity f = ContextExtKt.f(context);
|
Activity f = ContextExtKt.f(context);
|
||||||
@@ -132,33 +139,27 @@ public final class MentionsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a() {
|
public final void a() {
|
||||||
this.mDialogMentionComponent.g();
|
this.mDialogMentionComponent.g();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void b() {
|
public final void b() {
|
||||||
this.mDialogMentionComponent.h();
|
this.mDialogMentionComponent.h();
|
||||||
}
|
}
|
||||||
|
|
||||||
public b c() {
|
public final b c() {
|
||||||
return this.k;
|
return this.k;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int d() {
|
public final int d() {
|
||||||
return this.i;
|
return this.i;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void e() {
|
public final void e() {
|
||||||
this.mDialogMentionComponent.q();
|
this.mDialogMentionComponent.q();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void f() {
|
public final void f() {
|
||||||
this.mDialogMentionComponent.r();
|
this.mDialogMentionComponent.r();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface b {
|
|
||||||
boolean c();
|
|
||||||
|
|
||||||
boolean d();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -12,15 +12,15 @@ import android.view.ViewGroup;
|
|||||||
import com.vk.im.ui.o;
|
import com.vk.im.ui.o;
|
||||||
|
|
||||||
public class StoryBorderView extends View {
|
public class StoryBorderView extends View {
|
||||||
private final Paint a;
|
|
||||||
private final int g;
|
|
||||||
private final int h;
|
|
||||||
private boolean B;
|
private boolean B;
|
||||||
|
private final Paint a;
|
||||||
private LinearGradient f15747b;
|
private LinearGradient f15747b;
|
||||||
private int f15748c;
|
private int f15748c;
|
||||||
private int f15749d;
|
private int f15749d;
|
||||||
private int f15750e;
|
private int f15750e;
|
||||||
private int f15751f;
|
private int f15751f;
|
||||||
|
private final int g;
|
||||||
|
private final int h;
|
||||||
|
|
||||||
public StoryBorderView(Context context) {
|
public StoryBorderView(Context context) {
|
||||||
this(context, null, 0, 6);
|
this(context, null, 0, 6);
|
||||||
@@ -34,32 +34,10 @@ public class StoryBorderView extends View {
|
|||||||
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoryBorderView(Context context, AttributeSet attributeSet, int i) {
|
|
||||||
super(context, attributeSet, i);
|
|
||||||
Paint paint = new Paint();
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
|
||||||
this.a = paint;
|
|
||||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, o.StoryBorderView);
|
|
||||||
setBorderColor(obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderColor, 0));
|
|
||||||
this.g = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientStartColor, 0);
|
|
||||||
this.h = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientEndColor, 0);
|
|
||||||
setBorderWidth(obtainStyledAttributes.getDimensionPixelSize(o.StoryBorderView_vkim_borderWidth, 0));
|
|
||||||
obtainStyledAttributes.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBorderColor() {
|
public int getBorderColor() {
|
||||||
return this.f15749d;
|
return this.f15749d;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBorderColor(int i) {
|
|
||||||
if (this.f15749d != i) {
|
|
||||||
this.f15749d = i;
|
|
||||||
this.a.setColor(i);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBorderGradientEndColor() {
|
public int getBorderGradientEndColor() {
|
||||||
return this.h;
|
return this.h;
|
||||||
}
|
}
|
||||||
@@ -72,33 +50,14 @@ public class StoryBorderView extends View {
|
|||||||
return this.f15750e;
|
return this.f15750e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBorderWidth(int i) {
|
|
||||||
if (this.f15750e != i) {
|
|
||||||
this.f15750e = i;
|
|
||||||
this.a.setStrokeWidth(i);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPadding() {
|
public int getPadding() {
|
||||||
return this.f15751f;
|
return this.f15751f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPadding(int i) {
|
|
||||||
if (this.f15751f != i) {
|
|
||||||
this.f15751f = i;
|
|
||||||
setPadding(i, i, i, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getViewSize() {
|
public int getViewSize() {
|
||||||
return this.f15748c;
|
return this.f15748c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setViewSize(int i) {
|
|
||||||
this.f15748c = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
canvas.drawOval(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), this.a);
|
canvas.drawOval(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), this.a);
|
||||||
@@ -110,6 +69,22 @@ public class StoryBorderView extends View {
|
|||||||
setMeasuredDimension(i3, i3);
|
setMeasuredDimension(i3, i3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBorderColor(int i) {
|
||||||
|
if (this.f15749d != i) {
|
||||||
|
this.f15749d = i;
|
||||||
|
this.a.setColor(i);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBorderWidth(int i) {
|
||||||
|
if (this.f15750e != i) {
|
||||||
|
this.f15750e = i;
|
||||||
|
this.a.setStrokeWidth(i);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLayoutParams(ViewGroup.LayoutParams layoutParams) {
|
public void setLayoutParams(ViewGroup.LayoutParams layoutParams) {
|
||||||
super.setLayoutParams(layoutParams);
|
super.setLayoutParams(layoutParams);
|
||||||
@@ -133,4 +108,29 @@ public class StoryBorderView extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPadding(int i) {
|
||||||
|
if (this.f15751f != i) {
|
||||||
|
this.f15751f = i;
|
||||||
|
setPadding(i, i, i, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewSize(int i) {
|
||||||
|
this.f15748c = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoryBorderView(Context context, AttributeSet attributeSet, int i) {
|
||||||
|
super(context, attributeSet, i);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
this.a = paint;
|
||||||
|
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, o.StoryBorderView);
|
||||||
|
setBorderColor(obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderColor, 0));
|
||||||
|
this.g = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientStartColor, 0);
|
||||||
|
this.h = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientEndColor, 0);
|
||||||
|
setBorderWidth(obtainStyledAttributes.getDimensionPixelSize(o.StoryBorderView_vkim_borderWidth, 0));
|
||||||
|
obtainStyledAttributes.recycle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
package com.vk.libvideo;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import com.vk.bridges.StatisticsBridge1;
|
|
||||||
import com.vk.dto.common.VideoFile;
|
|
||||||
import com.vk.media.player.PlayerTypes;
|
|
||||||
import com.vk.navigation.NavigatorKeys;
|
|
||||||
import com.vk.statistic.Statistic;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
import ru.vtosters.lite.utils.Metrics;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public class VideoTracker {
|
|
||||||
private final VideoFile a;
|
|
||||||
|
|
||||||
/* renamed from: b reason: collision with root package name */
|
|
||||||
private Statistic f15638b;
|
|
||||||
|
|
||||||
/* renamed from: c reason: collision with root package name */
|
|
||||||
private String f15639c;
|
|
||||||
|
|
||||||
/* renamed from: d reason: collision with root package name */
|
|
||||||
private String f15640d;
|
|
||||||
|
|
||||||
/* renamed from: e reason: collision with root package name */
|
|
||||||
private final boolean f15641e;
|
|
||||||
|
|
||||||
/* renamed from: f reason: collision with root package name */
|
|
||||||
private int f15642f = -1;
|
|
||||||
private final int g;
|
|
||||||
private final int h;
|
|
||||||
private final int i;
|
|
||||||
private final int j;
|
|
||||||
private final int k;
|
|
||||||
private int l;
|
|
||||||
private PlayerType m;
|
|
||||||
private Screen n;
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public enum FullscreenTransition {
|
|
||||||
TAP,
|
|
||||||
SCREEN_ROTATION
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public enum PlayerType {
|
|
||||||
INLINE,
|
|
||||||
CAROUSEL,
|
|
||||||
FULLSCREEN
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public enum ResizeAction {
|
|
||||||
BUTTON,
|
|
||||||
GESTURE
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public enum RewindType {
|
|
||||||
SLIDER,
|
|
||||||
DOUBLE_TAP
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loaded from: classes3.dex */
|
|
||||||
public enum Screen {
|
|
||||||
INLINE,
|
|
||||||
CAROUSEL,
|
|
||||||
PORTRAIT,
|
|
||||||
LANDSCAPE
|
|
||||||
}
|
|
||||||
|
|
||||||
public VideoTracker(VideoFile videoFile, @Nullable Statistic statistic, String str, boolean z, @Nullable String str2) {
|
|
||||||
this.a = videoFile;
|
|
||||||
this.f15638b = statistic;
|
|
||||||
int i = videoFile.d;
|
|
||||||
this.g = (int) (i * 0.25f);
|
|
||||||
this.h = (int) (i * 0.5f);
|
|
||||||
this.i = (int) (i * 0.75f);
|
|
||||||
this.j = (int) (i * 0.95f);
|
|
||||||
this.k = i - 1;
|
|
||||||
this.f15639c = str;
|
|
||||||
this.f15641e = z;
|
|
||||||
this.f15640d = str2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean a(int i, int i2, int i3) {
|
|
||||||
return i3 > i && i3 <= i2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(String str) {
|
|
||||||
this.f15640d = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean b() {
|
|
||||||
return this.f15638b != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void c() {
|
|
||||||
this.f15642f = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void d() {
|
|
||||||
c("video_fullscreen_off");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void e() {
|
|
||||||
c("video_fullscreen_on");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void f() {
|
|
||||||
c("video_pause");
|
|
||||||
c("pause", "pause");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void g() {
|
|
||||||
c("video_resume");
|
|
||||||
c("pause", "resume");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void h() {
|
|
||||||
c("video_volume_off");
|
|
||||||
c("volume", "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void i() {
|
|
||||||
c("video_volume_on");
|
|
||||||
c("volume", "on");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void c(String str, String str2) {
|
|
||||||
a(b(str, str2));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String a() {
|
|
||||||
return this.f15640d;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void b(String str) {
|
|
||||||
if (Objects.equals(this.f15639c, str)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.f15639c = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void c(String str) {
|
|
||||||
if (this.f15638b != null) {
|
|
||||||
StatisticsBridge1.a().a(this.f15638b, str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(@Nullable Statistic statistic) {
|
|
||||||
this.f15638b = statistic;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bundle b(String str, String str2) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString(NotificationCompat.CATEGORY_EVENT, str);
|
|
||||||
bundle.putString("value", str2);
|
|
||||||
bundle.putInt("position", this.f15642f);
|
|
||||||
return bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(PlayerType playerType) {
|
|
||||||
if (this.m != playerType) {
|
|
||||||
this.m = playerType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(int i, int i2) {
|
|
||||||
int i3 = this.f15642f;
|
|
||||||
if (i == i3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (a(i3, i, 0)) {
|
|
||||||
a("start", i2);
|
|
||||||
c("video_start");
|
|
||||||
}
|
|
||||||
// if (this.a.d > 0) {
|
|
||||||
// if (a(this.f15642f, i, 3)) {
|
|
||||||
// a("3s", i2);
|
|
||||||
// c("video_play_3s");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, 10)) {
|
|
||||||
// a("10s", i2);
|
|
||||||
// c("video_play_10s");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, this.g)) {
|
|
||||||
// a("25", i2);
|
|
||||||
// c("video_play_25");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, this.h)) {
|
|
||||||
// a("50", i2);
|
|
||||||
// c("video_play_50");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, this.i)) {
|
|
||||||
// a("75", i2);
|
|
||||||
// c("video_play_75");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, this.j)) {
|
|
||||||
// a("95", i2);
|
|
||||||
// c("video_play_95");
|
|
||||||
// }
|
|
||||||
// if (a(this.f15642f, i, this.k)) {
|
|
||||||
// a("100", i2);
|
|
||||||
// c("video_play_100");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
this.f15642f = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(int i) {
|
|
||||||
a("continue", i);
|
|
||||||
c("video_start");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(@NonNull Screen screen) {
|
|
||||||
if (this.n != screen) {
|
|
||||||
this.n = screen;
|
|
||||||
c("screen", screen.toString().toLowerCase());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(@NonNull FullscreenTransition fullscreenTransition) {
|
|
||||||
c("full_screen_transition", fullscreenTransition.toString().toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(int i, boolean z) {
|
|
||||||
if (this.l != i) {
|
|
||||||
this.l = i;
|
|
||||||
c(z ? "auto_quality" : "quality", PlayerTypes.c(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(@Nullable String str, boolean z) {
|
|
||||||
String str2 = z ? "auto_subtitles" : "subtitles";
|
|
||||||
if (str == null) {
|
|
||||||
str = "off";
|
|
||||||
}
|
|
||||||
c(str2, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(int i, int i2, @NonNull RewindType rewindType) {
|
|
||||||
Bundle b2 = b("rewind", i <= i2 ? "forward" : "backward");
|
|
||||||
b2.putString("rewind_type", rewindType.toString().toLowerCase());
|
|
||||||
b2.putInt("rewind_start", i);
|
|
||||||
b2.putInt("rewind_end", i2);
|
|
||||||
a(b2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(float f2) {
|
|
||||||
String format;
|
|
||||||
if (f2 == 1.0f) {
|
|
||||||
format = "normal";
|
|
||||||
} else {
|
|
||||||
int i = (int) f2;
|
|
||||||
format = f2 == ((float) i) ? String.format(Locale.US, "%s", i) : String.format(Locale.US, "%.2f", f2);
|
|
||||||
}
|
|
||||||
c("speed", format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(boolean z, @NonNull ResizeAction resizeAction) {
|
|
||||||
Bundle b2 = b("resize", z ? "widen" : "narrow");
|
|
||||||
b2.putString("resize_action", resizeAction.toString().toLowerCase());
|
|
||||||
a(b2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(String str, String str2) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString("link_type", str);
|
|
||||||
bundle.putString("value", str2);
|
|
||||||
bundle.putString(NotificationCompat.CATEGORY_EVENT, "show_link");
|
|
||||||
a(bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void a(String str, int i) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString("autoplay", this.f15641e ? "1" : "0");
|
|
||||||
bundle.putString("position", str);
|
|
||||||
if (i >= 0) {
|
|
||||||
bundle.putInt("quality", i - 2);
|
|
||||||
}
|
|
||||||
PlayerType playerType = this.m;
|
|
||||||
if (playerType != null) {
|
|
||||||
bundle.putString("player_type", playerType.toString().toLowerCase());
|
|
||||||
}
|
|
||||||
a("video_play", bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void a(Bundle bundle) {
|
|
||||||
a("video_event", bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void a(String str, Bundle bundle) {
|
|
||||||
bundle.putString("video_id", this.a.a + "_" + this.a.b);
|
|
||||||
bundle.putString(NavigatorKeys.V, this.f15639c);
|
|
||||||
bundle.putString(NavigatorKeys.Z, this.f15639c);
|
|
||||||
if (!TextUtils.isEmpty(this.f15640d)) {
|
|
||||||
bundle.putString("context", this.f15640d);
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(this.a.l0)) {
|
|
||||||
bundle.putString(NavigatorKeys.l0, this.a.l0);
|
|
||||||
}
|
|
||||||
bundle.putLong("ts", System.currentTimeMillis());
|
|
||||||
StatisticsBridge1.a().a(str, bundle);
|
|
||||||
|
|
||||||
if (Preferences.getBoolValue("sendVideoMetrics", true) && str.equals("video_play")) {
|
|
||||||
try {
|
|
||||||
sendMetrics(str);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendMetrics(String event) throws JSONException {
|
|
||||||
JSONObject jsonObject = new JSONObject();
|
|
||||||
jsonObject.put("e", event);
|
|
||||||
jsonObject.put("source", this.f15639c);
|
|
||||||
jsonObject.put("ref", this.f15639c);
|
|
||||||
jsonObject.put("video_id", this.a.a + "_" + this.a.b);
|
|
||||||
jsonObject.put("position", "start");
|
|
||||||
jsonObject.put("position_sec", 0);
|
|
||||||
jsonObject.put("cur_quality", "auto");
|
|
||||||
|
|
||||||
Metrics.trackEvents(jsonObject, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,8 +24,8 @@ public class MediaImageEncoder {
|
|||||||
return compressBitmap(bitmap, file, CompressFormat.JPEG, quality);
|
return compressBitmap(bitmap, file, CompressFormat.JPEG, quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean needToCompress() {
|
private static boolean compressBitmapLossless(Bitmap bitmap, File file) {
|
||||||
return Preferences.getPreferences().getBoolean("compressPhotos", true);
|
return compressBitmap(bitmap, file, CompressFormat.PNG, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean compressBitmapNative(Bitmap bitmap, File file, int quality) {
|
private static boolean compressBitmapNative(Bitmap bitmap, File file, int quality) {
|
||||||
@@ -37,7 +37,7 @@ public class MediaImageEncoder {
|
|||||||
byte[] jpegData = MediaNative.compressBitmapJpeg(bitmap, quality);
|
byte[] jpegData = MediaNative.compressBitmapJpeg(bitmap, quality);
|
||||||
|
|
||||||
if (jpegData == null) {
|
if (jpegData == null) {
|
||||||
return compressBitmapJpeg(bitmap, file, quality);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
||||||
@@ -51,21 +51,43 @@ public class MediaImageEncoder {
|
|||||||
|
|
||||||
public static boolean encodePicture(Bitmap bitmap, File file, int quality) {
|
public static boolean encodePicture(Bitmap bitmap, File file, int quality) {
|
||||||
if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
|
if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
|
||||||
return compressBitmapNative(bitmap, file, quality);
|
switch (Preferences.getPreferences().getString("compression", "jpeg")) {
|
||||||
|
case "skip" -> {
|
||||||
|
return compressBitmapLossless(bitmap, file);
|
||||||
|
}
|
||||||
|
case "jpeg" -> {
|
||||||
|
return compressBitmapJpeg(bitmap, file, quality);
|
||||||
|
}
|
||||||
|
case "native" -> {
|
||||||
|
return compressBitmapNative(bitmap, file, quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean needToSkipCompression() {
|
||||||
|
return Preferences.getPreferences().getString("compression", "jpeg").equals("skip");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean needToCompress() {
|
||||||
|
return !needToSkipCompression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean encodeJpeg(Bitmap bitmap, File file) {
|
||||||
|
return encodePicture(bitmap, file, 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean encodeJpegWithoutCompression(Bitmap bitmap, File file) {
|
||||||
|
return encodePicture(bitmap, file, 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean encodeJpeg(Bitmap bitmap, File file, int quality) {
|
||||||
|
if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
|
||||||
|
return needToCompress() ? compressBitmapNative(bitmap, file, quality) : compressBitmapJpeg(bitmap, file, quality);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static boolean encodeJpeg(Bitmap bitmap, File file) {
|
|
||||||
return encodePicture(bitmap, file, Preferences.compress(85));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean encodeJpegWithoutCompression(Bitmap bitmap, File file) {
|
|
||||||
return encodePicture(bitmap, file, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean encodeJpeg(Bitmap bitmap, File file, int quality) {
|
|
||||||
return encodePicture(bitmap, file, Preferences.compress(85));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ public final class MediaNative {
|
|||||||
if (isMediaLoaded) {
|
if (isMediaLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
NativeLibLoader.loadLibrary("ffmpeg");
|
||||||
isMediaLoaded = NativeLibLoader.loadLibrary("vkmedia");
|
isMediaLoaded = NativeLibLoader.loadLibrary("vkmedia");
|
||||||
isMediaLoaded = NativeLibLoader.loadLibrary("vkmediaencoder");
|
isMediaLoaded = NativeLibLoader.loadLibrary("vkmediaencoder");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,256 +0,0 @@
|
|||||||
package com.vk.music.stats;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import com.vk.core.extensions.StringExt;
|
|
||||||
import com.vk.music.common.MusicPlaybackLaunchContext;
|
|
||||||
import com.vk.music.logger.MusicLogger;
|
|
||||||
import com.vk.music.stats.i.MusicPlaybackParams;
|
|
||||||
import com.vk.navigation.NavigatorKeys;
|
|
||||||
import com.vtosters.lite.data.Analytics;
|
|
||||||
import kotlin.jvm.b.Functions2;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
import ru.vtosters.lite.utils.Metrics;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public final class VKMusicStatsTracker implements MusicStatsTracker {
|
|
||||||
private String a;
|
|
||||||
private final Functions2<String, Analytics.l> b;
|
|
||||||
private static final a c = new a();
|
|
||||||
|
|
||||||
public VKMusicStatsTracker(Functions2<String, Analytics.l> functions2) {
|
|
||||||
this.b = functions2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String e(MusicPlaybackParams musicPlaybackParams) {
|
|
||||||
String g = musicPlaybackParams.g();
|
|
||||||
if (g == null) {
|
|
||||||
g = this.a;
|
|
||||||
}
|
|
||||||
if (g == null) {
|
|
||||||
g = "auto";
|
|
||||||
}
|
|
||||||
this.a = g;
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(boolean z) {
|
|
||||||
MusicStatsTracker.a.a(this, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(MusicPlaybackParams musicPlaybackParams) {
|
|
||||||
c.a(a(musicPlaybackParams, "music_start_playback"));
|
|
||||||
|
|
||||||
if (Preferences.sendMusicMetrics()) {
|
|
||||||
try {
|
|
||||||
makeMetricsRequest(musicPlaybackParams, "music_start_playback");
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(boolean z) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void c(MusicPlaybackParams musicPlaybackParams) {
|
|
||||||
MusicStatsTracker.a.b(this, musicPlaybackParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void d() {
|
|
||||||
MusicStatsTracker.a.a(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Intent intent, String str) {
|
|
||||||
MusicStatsTracker.a.a(this, intent, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(String str, @NonNull String str2) {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription_action");
|
|
||||||
invoke.a("popup", str);
|
|
||||||
invoke.a("action", str2);
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void c(boolean z) {
|
|
||||||
MusicStatsTracker.a.b(this, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void d(MusicPlaybackParams musicPlaybackParams) {
|
|
||||||
if (Objects.equals("pause", this.a)) {
|
|
||||||
a(musicPlaybackParams);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(long j) {
|
|
||||||
MusicStatsTracker.a.a(this, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void c() {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription_push");
|
|
||||||
invoke.a("action", "open");
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(String str, MusicStatsRefer musicStatsRefer) {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription_show");
|
|
||||||
invoke.a("popup", str);
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b() {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription_push");
|
|
||||||
invoke.a("action", "send");
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(MusicPlaybackParams musicPlaybackParams) {
|
|
||||||
c.a(a(musicPlaybackParams, "music_stop_playback"));
|
|
||||||
|
|
||||||
if (Preferences.sendMusicMetrics()) {
|
|
||||||
try {
|
|
||||||
makeMetricsRequest(musicPlaybackParams, "music_stop_playback");
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PlayerStateChangedParams playerStateChangedParams) {
|
|
||||||
Analytics.l invoke = this.b.invoke("audio_player");
|
|
||||||
invoke.a(NavigatorKeys.t0, playerStateChangedParams.a());
|
|
||||||
invoke.a("prev_state", playerStateChangedParams.c());
|
|
||||||
invoke.a("duration", playerStateChangedParams.b());
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(String str) {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription_free_displayed");
|
|
||||||
invoke.a("popup", str);
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(String str) {
|
|
||||||
Analytics.l invoke = this.b.invoke("playlist_start");
|
|
||||||
invoke.a(NavigatorKeys.e, str);
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void makeMetricsRequest(MusicPlaybackParams musicPlaybackParams, String str) throws JSONException {
|
|
||||||
JSONObject event = new JSONObject();
|
|
||||||
|
|
||||||
if (str.equals("music_stop_playback")) {
|
|
||||||
event.put("e", str);
|
|
||||||
event.put("audio_id", musicPlaybackParams.c());
|
|
||||||
event.put("duration", musicPlaybackParams.a());
|
|
||||||
event.put("start_time", musicPlaybackParams.i());
|
|
||||||
event.put("shuffle", "false");
|
|
||||||
event.put("reason", "new");
|
|
||||||
event.put("state", "app");
|
|
||||||
event.put("duration", musicPlaybackParams.a());
|
|
||||||
} else {
|
|
||||||
event.put("e", str);
|
|
||||||
event.put("audio_id", musicPlaybackParams.c());
|
|
||||||
event.put("start_time", musicPlaybackParams.i());
|
|
||||||
event.put("shuffle", "false");
|
|
||||||
event.put("reason", "auto");
|
|
||||||
event.put("state", "app");
|
|
||||||
|
|
||||||
if (StringExt.a((CharSequence) musicPlaybackParams.e())) {
|
|
||||||
event.put("prev_audio_id", musicPlaybackParams.e());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Metrics.trackEvents(event, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Analytics.l a(MusicPlaybackParams musicPlaybackParams, String str) {
|
|
||||||
Analytics.l invoke = this.b.invoke(str);
|
|
||||||
invoke.a("audio_id", musicPlaybackParams.c());
|
|
||||||
invoke.a("uuid", UUID.randomUUID().hashCode());
|
|
||||||
invoke.a("shuffle", musicPlaybackParams.l());
|
|
||||||
invoke.a("reason", e(musicPlaybackParams));
|
|
||||||
invoke.a("start_time", musicPlaybackParams.i());
|
|
||||||
invoke.a("playback_started_at", musicPlaybackParams.d());
|
|
||||||
invoke.a(NavigatorKeys.l0, musicPlaybackParams.j());
|
|
||||||
if (!Objects.equals("music_start_playback", str)) {
|
|
||||||
invoke.a("duration", musicPlaybackParams.a());
|
|
||||||
}
|
|
||||||
int i = h.$EnumSwitchMapping$0[musicPlaybackParams.b().ordinal()];
|
|
||||||
if (i == 1) {
|
|
||||||
invoke.a("repeat", "one");
|
|
||||||
} else if (i == 2) {
|
|
||||||
invoke.a("repeat", "all");
|
|
||||||
}
|
|
||||||
invoke.a(NavigatorKeys.t0, musicPlaybackParams.m());
|
|
||||||
MusicPlaybackLaunchContext h = musicPlaybackParams.h();
|
|
||||||
invoke.a(NavigatorKeys.V, h.v0());
|
|
||||||
if (h.v1()) {
|
|
||||||
invoke.a("playlist_id", h.u1());
|
|
||||||
}
|
|
||||||
if (h.i(4) || h.i(8)) {
|
|
||||||
invoke.a("expanded", h.i(4));
|
|
||||||
}
|
|
||||||
if (StringExt.a((CharSequence) musicPlaybackParams.e())) {
|
|
||||||
invoke.a("prev_audio_id", musicPlaybackParams.e());
|
|
||||||
}
|
|
||||||
if (StringExt.a((CharSequence) musicPlaybackParams.f())) {
|
|
||||||
invoke.a("prev_playlist_id", musicPlaybackParams.f());
|
|
||||||
}
|
|
||||||
return invoke;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(String str, String str2) {
|
|
||||||
boolean a2 = Objects.equals("success", str2);
|
|
||||||
Analytics.l builder = this.b.invoke("music_subscription_purchase_result");
|
|
||||||
builder.a("popup", str);
|
|
||||||
builder.a(NotificationCompat.CATEGORY_STATUS, a2 ? "success" : "fail");
|
|
||||||
if (!a2) {
|
|
||||||
builder.a("reason", str2);
|
|
||||||
}
|
|
||||||
c.a(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a() {
|
|
||||||
Analytics.l invoke = this.b.invoke("music_subscription");
|
|
||||||
invoke.a("popup", "background_v2");
|
|
||||||
invoke.a("action", "link");
|
|
||||||
c.a(invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class a {
|
|
||||||
private a() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void a(Analytics.l lVar) {
|
|
||||||
MusicLogger.a("MusicStats", "[VK_TRACKER]", lVar);
|
|
||||||
lVar.b();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -156,7 +156,7 @@ public class LyricsHolder extends RecyclerHolder<PlayerInfo> implements View.OnC
|
|||||||
|
|
||||||
@Override // com.vk.api.base.ApiCallback
|
@Override // com.vk.api.base.ApiCallback
|
||||||
public void a(VKApiExecutionException vKApiExecutionException) {
|
public void a(VKApiExecutionException vKApiExecutionException) {
|
||||||
if (Preferences.getBoolValue("useGenius", false) && !Preferences.serverFeaturesDisable()) {
|
if (Preferences.getBoolValue("useGenius", false)) {
|
||||||
var text = Genius.getTextMusic(a);
|
var text = Genius.getTextMusic(a);
|
||||||
var lyrics = new AudioGetLyrics.a();
|
var lyrics = new AudioGetLyrics.a();
|
||||||
lyrics.a = text;
|
lyrics.a = text;
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
package com.vk.newsfeed.holders;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
|
||||||
import com.vk.core.ui.themes.VKThemeHelper;
|
|
||||||
import com.vk.dto.newsfeed.entries.NewsEntry;
|
|
||||||
import com.vk.dto.newsfeed.entries.Post;
|
|
||||||
import com.vk.dto.newsfeed.entries.PromoPost;
|
|
||||||
import com.vk.extensions.ViewExtKt;
|
|
||||||
import com.vtosters.lite.R;
|
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
|
||||||
import ru.vtosters.lite.utils.newsfeed.NewsFeedFiltersUtils;
|
|
||||||
import ru.vtosters.sponsorpost.internal.Native;
|
|
||||||
import ru.vtosters.sponsorpost.internal.VotesPreferences;
|
|
||||||
import ru.vtosters.sponsorpost.internal.VotesService;
|
|
||||||
import ru.vtosters.sponsorpost.utils.PostsPreferences;
|
|
||||||
|
|
||||||
public final class AdMarkHolder extends BaseNewsEntryHolder<NewsEntry> {
|
|
||||||
private final TextView F;
|
|
||||||
|
|
||||||
@SuppressLint("ResourceType")
|
|
||||||
public AdMarkHolder(ViewGroup viewGroup) {
|
|
||||||
super(R.layout.marked_as_ads, viewGroup);
|
|
||||||
View view = ((RecyclerView.ViewHolder) this).itemView;
|
|
||||||
this.F = ViewExtKt.a(view, R.id.text, (View.OnClickListener) null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ResourceType")
|
|
||||||
public void b(NewsEntry newsEntry) { // onBind
|
|
||||||
String disclamer = null;
|
|
||||||
Drawable drawable = null;
|
|
||||||
int height = 0;
|
|
||||||
|
|
||||||
if (newsEntry instanceof Post) {
|
|
||||||
Post post = ((Post) newsEntry);
|
|
||||||
if (post.L1()) {
|
|
||||||
drawable = VKThemeHelper.a(R.drawable.marked_as_ads, R.attr.icon_tertiary);
|
|
||||||
int ownerId = post.b();
|
|
||||||
int postId = post.P1();
|
|
||||||
int date = post.K();
|
|
||||||
String text = post.getText();
|
|
||||||
|
|
||||||
if (PostsPreferences.isPostAd(ownerId, postId)) {
|
|
||||||
disclamer = "SponsorPost: Реклама";
|
|
||||||
if (Preferences.isValidSignature() && Native.canVote()) {
|
|
||||||
this.F.setOnClickListener(listener -> voteDialog(ownerId, postId, date, this.F.getContext()));
|
|
||||||
}
|
|
||||||
} else if (VotesPreferences.isPostAd(ownerId, postId)) {
|
|
||||||
disclamer = "SponsorPost: Возможно реклама";
|
|
||||||
if (Preferences.isValidSignature() && Native.canVote()) {
|
|
||||||
this.F.setOnClickListener(listener -> voteDialog(ownerId, postId, date, this.F.getContext()));
|
|
||||||
}
|
|
||||||
} else if (NewsFeedFiltersUtils.sponsorFilters(text)) {
|
|
||||||
disclamer = "SponsorPost: Заблокировано фильтрами";
|
|
||||||
this.F.setOnClickListener(listener -> AndroidUtils.sendToast("Заблокировано по выражению: " + NewsFeedFiltersUtils.sponsorFiltersBanWord(text)));
|
|
||||||
} else {
|
|
||||||
disclamer = m(R.string.sponsored_post_in_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
height = e0().getDimensionPixelSize(R.dimen.newsfeed_ad_mark_post_height);
|
|
||||||
}
|
|
||||||
} else if (newsEntry instanceof PromoPost) {
|
|
||||||
PromoPost promoPost = (PromoPost) newsEntry;
|
|
||||||
if (!promoPost.E1().isEmpty()) {
|
|
||||||
height = e0().getDimensionPixelSize(R.dimen.newsfeed_ad_mark_promo_post_height);
|
|
||||||
disclamer = promoPost.E1();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.F.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null);
|
|
||||||
this.F.setMinHeight(height);
|
|
||||||
this.F.setText(disclamer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void voteDialog(int ownerId, int postId, int date, Context ctx) {
|
|
||||||
new VkAlertDialog.Builder(ctx)
|
|
||||||
.setTitle("SponsorPost")
|
|
||||||
.setMessage("Этот пост является рекламным?")
|
|
||||||
.setPositiveButton("Да", ((dialog, which) -> {
|
|
||||||
int resp = VotesService.ratePost(ownerId, postId, date, true).optInt("code");
|
|
||||||
|
|
||||||
if (resp == 201 || resp == 100) {
|
|
||||||
AndroidUtils.sendToast("Спасибо за голос!");
|
|
||||||
} else {
|
|
||||||
AndroidUtils.sendToast("Ваш голос уже учтен");
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.setNeutralButton("Нет", ((dialog, which) -> {
|
|
||||||
int resp = VotesService.ratePost(ownerId, postId, date, false).optInt("code");
|
|
||||||
|
|
||||||
if (resp == 201 || resp == 100) {
|
|
||||||
AndroidUtils.sendToast("Спасибо за голос!");
|
|
||||||
} else {
|
|
||||||
AndroidUtils.sendToast("Ваш голос уже учтен");
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,37 +36,37 @@ public final class CommunityBorderedImageView extends BorderedCircleImageView im
|
|||||||
|
|
||||||
public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i) {
|
public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i) {
|
||||||
super(context, attributeSet, i);
|
super(context, attributeSet, i);
|
||||||
this.g0 = VKThemeHelper.d(R.attr.accent);
|
this.g0 = VKThemeHelper.d((int) R.attr.accent);
|
||||||
this.h0 = ContextExtKt.a(context, R.color.gray_200);
|
this.h0 = ContextExtKt.a(context, (int) R.color.gray_200);
|
||||||
this.i0 = new PorterDuffColorFilter(this.g0, PorterDuff.Mode.SRC_IN);
|
this.i0 = new PorterDuffColorFilter(this.g0, PorterDuff.Mode.SRC_IN);
|
||||||
this.j0 = new PorterDuffColorFilter(this.h0, PorterDuff.Mode.SRC_IN);
|
this.j0 = new PorterDuffColorFilter(this.h0, PorterDuff.Mode.SRC_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void c(int unread, int read) { // get unread and read resid
|
public final void c(int unread, int read) { // get unread and read resid
|
||||||
this.unread = unread;
|
this.unread = unread;
|
||||||
this.read = read;
|
this.read = read;
|
||||||
this.T = BitmapFactory.decodeResource(getResources(), this.unread);
|
this.T = BitmapFactory.decodeResource(getResources(), this.unread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPrimaryColor() {
|
public final int getPrimaryColor() {
|
||||||
return this.g0;
|
return this.g0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPrimaryColor(int i) {
|
public final void setPrimaryColor(int i) {
|
||||||
this.g0 = i;
|
this.g0 = i;
|
||||||
this.i0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
this.i0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getWasViewedColor() {
|
public final int getWasViewedColor() {
|
||||||
return this.h0;
|
return this.h0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWasViewedColor(int i) {
|
public final void setWasViewedColor(int i) {
|
||||||
this.h0 = i;
|
this.h0 = i;
|
||||||
this.j0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
this.j0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void o() {
|
public final void o() {
|
||||||
this.f0 = false;
|
this.f0 = false;
|
||||||
l();
|
l();
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ public final class CommunityBorderedImageView extends BorderedCircleImageView im
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStoryContainer(StoriesContainer storiesContainer) {
|
public final void setStoryContainer(StoriesContainer storiesContainer) {
|
||||||
if (storiesContainer.L1()) {
|
if (storiesContainer.L1()) {
|
||||||
this.f0 = true;
|
this.f0 = true;
|
||||||
m();
|
m();
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ public class TargetImageView extends VKMultiImageView {
|
|||||||
private static final int I = Screen.a(2);
|
private static final int I = Screen.a(2);
|
||||||
private final int B;
|
private final int B;
|
||||||
private final int C;
|
private final int C;
|
||||||
private final RectF f21629d;
|
|
||||||
private final Paint f21630e;
|
|
||||||
private final RectF g;
|
|
||||||
private final Paint h;
|
|
||||||
private Drawable D;
|
private Drawable D;
|
||||||
private String[] F;
|
private String[] F;
|
||||||
private int G;
|
private int G;
|
||||||
|
private final RectF f21629d;
|
||||||
|
private final Paint f21630e;
|
||||||
private Canvas f21631f;
|
private Canvas f21631f;
|
||||||
|
private final RectF g;
|
||||||
|
private final Paint h;
|
||||||
|
|
||||||
public TargetImageView(Context context, Drawable d) {
|
public TargetImageView(Context context, Drawable d) {
|
||||||
this(context, null, 0, 6);
|
this(context, null, 0, 6);
|
||||||
@@ -51,43 +51,6 @@ public class TargetImageView extends VKMultiImageView {
|
|||||||
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetImageView(Context context, AttributeSet attributeSet, int i) {
|
|
||||||
super(context, attributeSet, i);
|
|
||||||
this.f21629d = new RectF();
|
|
||||||
Paint paint = new Paint();
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setDither(true);
|
|
||||||
paint.setFilterBitmap(true);
|
|
||||||
this.f21630e = paint;
|
|
||||||
this.g = new RectF();
|
|
||||||
Paint paint2 = new Paint();
|
|
||||||
paint2.setAntiAlias(true);
|
|
||||||
paint2.setStrokeWidth(I);
|
|
||||||
paint2.setStyle(Paint.Style.STROKE);
|
|
||||||
this.h = paint2;
|
|
||||||
this.B = getAccentColor();
|
|
||||||
this.C = this.B;
|
|
||||||
Drawable c2 = ContextExtKt.c(context, R.drawable.ic_check_circle_composite_24_full);
|
|
||||||
if (c2 != null) {
|
|
||||||
this.D = c2;
|
|
||||||
this.F = new String[4];
|
|
||||||
int i2 = I * 2;
|
|
||||||
setPadding(i2, i2, i2, i2);
|
|
||||||
if (attributeSet != null) {
|
|
||||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, a0.TargetImageView);
|
|
||||||
setSelected(obtainStyledAttributes.getBoolean(0, false));
|
|
||||||
obtainStyledAttributes.recycle();
|
|
||||||
}
|
|
||||||
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.user_placeholder);
|
|
||||||
for (int i3 = 0; i3 <= 3; i3++) {
|
|
||||||
a();
|
|
||||||
DraweeHolder<GenericDraweeHierarchy> a3 = this.a.a(i3);
|
|
||||||
a3.b().e(drawable);
|
|
||||||
}
|
|
||||||
setScaleType(ScalingUtils.b.o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void e() {
|
private void e() {
|
||||||
int i = this.G;
|
int i = this.G;
|
||||||
for (int i2 = 0; i2 < i; i2++) {
|
for (int i2 = 0; i2 < i; i2++) {
|
||||||
@@ -288,4 +251,41 @@ public class TargetImageView extends VKMultiImageView {
|
|||||||
drawable.setBounds(i - drawable.getIntrinsicWidth(), i2 - this.D.getIntrinsicHeight(), i, i2);
|
drawable.setBounds(i - drawable.getIntrinsicWidth(), i2 - this.D.getIntrinsicHeight(), i, i2);
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TargetImageView(Context context, AttributeSet attributeSet, int i) {
|
||||||
|
super(context, attributeSet, i);
|
||||||
|
this.f21629d = new RectF();
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setDither(true);
|
||||||
|
paint.setFilterBitmap(true);
|
||||||
|
this.f21630e = paint;
|
||||||
|
this.g = new RectF();
|
||||||
|
Paint paint2 = new Paint();
|
||||||
|
paint2.setAntiAlias(true);
|
||||||
|
paint2.setStrokeWidth(I);
|
||||||
|
paint2.setStyle(Paint.Style.STROKE);
|
||||||
|
this.h = paint2;
|
||||||
|
this.B = getAccentColor();
|
||||||
|
this.C = this.B;
|
||||||
|
Drawable c2 = ContextExtKt.c(context, R.drawable.ic_check_circle_composite_24_full);
|
||||||
|
if (c2 != null) {
|
||||||
|
this.D = c2;
|
||||||
|
this.F = new String[4];
|
||||||
|
int i2 = I * 2;
|
||||||
|
setPadding(i2, i2, i2, i2);
|
||||||
|
if (attributeSet != null) {
|
||||||
|
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, a0.TargetImageView);
|
||||||
|
setSelected(obtainStyledAttributes.getBoolean(0, false));
|
||||||
|
obtainStyledAttributes.recycle();
|
||||||
|
}
|
||||||
|
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.user_placeholder);
|
||||||
|
for (int i3 = 0; i3 <= 3; i3++) {
|
||||||
|
a();
|
||||||
|
DraweeHolder<GenericDraweeHierarchy> a3 = this.a.a(i3);
|
||||||
|
a3.b().e(drawable);
|
||||||
|
}
|
||||||
|
setScaleType(ScalingUtils.b.o);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -104,25 +104,6 @@ public final class StickerDetailsAdapter extends RecyclerView.Adapter<RecyclerVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class d extends RecyclerView.ViewHolder {
|
|
||||||
private final VKStickerImageView mImage;
|
|
||||||
|
|
||||||
public d(ViewGroup viewGroup) {
|
|
||||||
super(new VKStickerImageView(viewGroup.getContext()));
|
|
||||||
this.mImage = (VKStickerImageView) itemView;
|
|
||||||
this.mImage.setAspectRatio(1.0f);
|
|
||||||
this.mImage.setClickable(true);
|
|
||||||
this.mImage.getHierarchy().a().setVisible(true, true);
|
|
||||||
this.mImage.getHierarchy().a(0);
|
|
||||||
this.mImage.setContentDescription(this.mImage.getContext().getString(R.string.stickers_accessibility_sticker));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(StickerItem stickerItem) {
|
|
||||||
this.mImage.a(stickerItem.h(Screen.a(80)));
|
|
||||||
this.mImage.setTag(R.id.id, stickerItem.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class c extends RecyclerView.ViewHolder {
|
private final class c extends RecyclerView.ViewHolder {
|
||||||
private final VKImageView mImageBg;
|
private final VKImageView mImageBg;
|
||||||
private final VKImageView mPackImage;
|
private final VKImageView mPackImage;
|
||||||
@@ -168,4 +149,23 @@ public final class StickerDetailsAdapter extends RecyclerView.Adapter<RecyclerVi
|
|||||||
this.mDescription.setText(stickerStockItem.C1());
|
this.mDescription.setText(stickerStockItem.C1());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class d extends RecyclerView.ViewHolder {
|
||||||
|
private final VKStickerImageView mImage;
|
||||||
|
|
||||||
|
public d(ViewGroup viewGroup) {
|
||||||
|
super(new VKStickerImageView(viewGroup.getContext()));
|
||||||
|
this.mImage = (VKStickerImageView) itemView;
|
||||||
|
this.mImage.setAspectRatio(1.0f);
|
||||||
|
this.mImage.setClickable(true);
|
||||||
|
this.mImage.getHierarchy().a().setVisible(true, true);
|
||||||
|
this.mImage.getHierarchy().a(0);
|
||||||
|
this.mImage.setContentDescription(this.mImage.getContext().getString(R.string.stickers_accessibility_sticker));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(StickerItem stickerItem) {
|
||||||
|
this.mImage.a(stickerItem.h(Screen.a(80)));
|
||||||
|
this.mImage.setTag(R.id.id, stickerItem.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +382,7 @@ public class StoryCircleImageView extends BorderedCircleImageView implements The
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pipelineDraweeControllerBuilder.a(() -> StoryCircleImageView.this.a(list));
|
pipelineDraweeControllerBuilder.a((Supplier<DataSource<CloseableReference<CloseableImage>>>) () -> StoryCircleImageView.this.a(list));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pipelineDraweeControllerBuilder.b(null);
|
pipelineDraweeControllerBuilder.b(null);
|
||||||
|
|||||||
@@ -2140,6 +2140,8 @@ public final class R {
|
|||||||
public static final int im_ic_msgdl = 0x7f040774; // 2130970484
|
public static final int im_ic_msgdl = 0x7f040774; // 2130970484
|
||||||
public static final int im_ic_msgreload = 0x7f040775; // 2130970485
|
public static final int im_ic_msgreload = 0x7f040775; // 2130970485
|
||||||
public static final int im_ic_stats = 0x7f040776; // 2130970486
|
public static final int im_ic_stats = 0x7f040776; // 2130970486
|
||||||
|
public static final int pinBackgroundColor = 0x7f040777; // 2130970487
|
||||||
|
public static final int pinTextColor = 0x7f040778; // 2130970488
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class bool {
|
public static final class bool {
|
||||||
@@ -2160,6 +2162,9 @@ public final class R {
|
|||||||
public static final int spb_default_reversed = 0x7f05000e; // 2131034126
|
public static final int spb_default_reversed = 0x7f05000e; // 2131034126
|
||||||
public static final int vk_is_tablet = 0x7f05000f; // 2131034127
|
public static final int vk_is_tablet = 0x7f05000f; // 2131034127
|
||||||
public static final int com_samsung_android_icon_container_has_icon_container = 0x7f050010; // 2131034128
|
public static final int com_samsung_android_icon_container_has_icon_container = 0x7f050010; // 2131034128
|
||||||
|
public static final int applock__fingerprint_service_enabled = 0x7f050011; // 2131034129
|
||||||
|
public static final int applock__item_password_chars_enabled = 0x7f050012; // 2131034130
|
||||||
|
public static final int applock__unlock_activity_return_allowed = 0x7f050013; // 2131034131
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class color {
|
public static final class color {
|
||||||
@@ -2968,6 +2973,13 @@ public final class R {
|
|||||||
public static final int yellow_light = 0x7f060323; // 2131100451
|
public static final int yellow_light = 0x7f060323; // 2131100451
|
||||||
public static final int yellow_sunflower = 0x7f060324; // 2131100452
|
public static final int yellow_sunflower = 0x7f060324; // 2131100452
|
||||||
public static final int iclauncher = 0x7f060325; // 2131100453
|
public static final int iclauncher = 0x7f060325; // 2131100453
|
||||||
|
public static final int applock__action_settings = 0x7f060327; // 2131100455
|
||||||
|
public static final int applock__activity_background = 0x7f060328; // 2131100456
|
||||||
|
public static final int applock__create_choose_option_bg = 0x7f060329; // 2131100457
|
||||||
|
public static final int applock__create_choose_option_bg_pressed = 0x7f06032a; // 2131100458
|
||||||
|
public static final int applock__description_text = 0x7f06032b; // 2131100459
|
||||||
|
public static final int applock__item_background = 0x7f06032c; // 2131100460
|
||||||
|
public static final int applock__item_text = 0x7f06032d; // 2131100461
|
||||||
public static final int abc_color_highlight_material = 0x7f060004; // 2131099652
|
public static final int abc_color_highlight_material = 0x7f060004; // 2131099652
|
||||||
public static final int alpha05 = 0x7f060326; // 2131100454
|
public static final int alpha05 = 0x7f060326; // 2131100454
|
||||||
}
|
}
|
||||||
@@ -3821,6 +3833,21 @@ public final class R {
|
|||||||
public static final int wrap_content = 0x7f07035b; // 2131166043
|
public static final int wrap_content = 0x7f07035b; // 2131166043
|
||||||
public static final int year_label_height = 0x7f07035c; // 2131166044
|
public static final int year_label_height = 0x7f07035c; // 2131166044
|
||||||
public static final int year_label_text_size = 0x7f07035d; // 2131166045
|
public static final int year_label_text_size = 0x7f07035d; // 2131166045
|
||||||
|
public static final int applock__action_settings = 0x7f07035e; // 2131166046
|
||||||
|
public static final int applock__action_settings_margin_top = 0x7f07035f; // 2131166047
|
||||||
|
public static final int applock__action_settings_padding = 0x7f070360; // 2131166048
|
||||||
|
public static final int applock__activity_padding = 0x7f070361; // 2131166049
|
||||||
|
public static final int applock__chooser_image_size = 0x7f070362; // 2131166050
|
||||||
|
public static final int applock__chooser_parent_padding_vertical = 0x7f070363; // 2131166051
|
||||||
|
public static final int applock__chooser_text_padding_top = 0x7f070364; // 2131166052
|
||||||
|
public static final int applock__content_padding_horizontal = 0x7f070365; // 2131166053
|
||||||
|
public static final int applock__content_padding_vertical = 0x7f070366; // 2131166054
|
||||||
|
public static final int applock__description_padding_bottom = 0x7f070367; // 2131166055
|
||||||
|
public static final int applock__description_text = 0x7f070368; // 2131166056
|
||||||
|
public static final int applock__fingerprint_scan_image_margin_top = 0x7f070369; // 2131166057
|
||||||
|
public static final int applock__fingerprint_scan_image_size = 0x7f07036a; // 2131166058
|
||||||
|
public static final int applock__item_margin_top = 0x7f07036b; // 2131166059
|
||||||
|
public static final int applock__item_size = 0x7f07036c; // 2131166060
|
||||||
public static final int vkim_article_blocked_bottom_margin = 0x7f070329; // 2131165993
|
public static final int vkim_article_blocked_bottom_margin = 0x7f070329; // 2131165993
|
||||||
public static final int vkim_article_blocked_ic_margin = 0x7f07032a; // 2131165994
|
public static final int vkim_article_blocked_ic_margin = 0x7f07032a; // 2131165994
|
||||||
public static final int vkim_article_blocked_ic_size = 0x7f07032b; // 2131165995
|
public static final int vkim_article_blocked_ic_size = 0x7f07032b; // 2131165995
|
||||||
@@ -4999,6 +5026,9 @@ public final class R {
|
|||||||
public static final int custom_thumb_selector = 0x7f080be9; // 2131233769
|
public static final int custom_thumb_selector = 0x7f080be9; // 2131233769
|
||||||
public static final int custom_track_selector = 0x7f080bea; // 2131233770
|
public static final int custom_track_selector = 0x7f080bea; // 2131233770
|
||||||
public static final int splash = 0x7f080bec; // 2131233772
|
public static final int splash = 0x7f080bec; // 2131233772
|
||||||
|
public static final int applock__chooser_option_bg = 0x7f080bee; // 2131233774
|
||||||
|
public static final int applock__fingerprint = 0x7f080bef; // 2131233775
|
||||||
|
public static final int applock__pin = 0x7f080bf0; // 2131233776
|
||||||
public static final int abc_control_background_material = 0x7f080032; // 2131230770
|
public static final int abc_control_background_material = 0x7f080032; // 2131230770
|
||||||
public static final int ic_action_close = 0x7f080322; // 2131231522
|
public static final int ic_action_close = 0x7f080322; // 2131231522
|
||||||
public static final int ic_camera_outline_28_new = 0x7f0803a0; // 2131231648
|
public static final int ic_camera_outline_28_new = 0x7f0803a0; // 2131231648
|
||||||
@@ -11062,6 +11092,12 @@ public final class R {
|
|||||||
public static final int color_preview_border = 0x7f0a1024; // 2131365924
|
public static final int color_preview_border = 0x7f0a1024; // 2131365924
|
||||||
public static final int account_backup_view = 0x7f0a1025; // 2131365925
|
public static final int account_backup_view = 0x7f0a1025; // 2131365925
|
||||||
public static final int copy_image = 0x7f0a1026; // 2131365926
|
public static final int copy_image = 0x7f0a1026; // 2131365926
|
||||||
|
public static final int applock__activity_lock_creation = 0x7f0a1027; // 2131365927
|
||||||
|
public static final int applock__activity_unlock = 0x7f0a1028; // 2131365928
|
||||||
|
public static final int pin__action_settings = 0x7f0a1029; // 2131365929
|
||||||
|
public static final int pin__container = 0x7f0a102a; // 2131365930
|
||||||
|
public static final int pin__description = 0x7f0a102b; // 2131365931
|
||||||
|
public static final int pin__input_view = 0x7f0a102c; // 2131365932
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class integer {
|
public static final class integer {
|
||||||
@@ -11108,6 +11144,10 @@ public final class R {
|
|||||||
public static final int status_bar_notification_info_maxnum = 0x7f0b002b; // 2131427371
|
public static final int status_bar_notification_info_maxnum = 0x7f0b002b; // 2131427371
|
||||||
public static final int vkim_activity_animation_duration = 0x7f0b002c; // 2131427372
|
public static final int vkim_activity_animation_duration = 0x7f0b002c; // 2131427372
|
||||||
public static final int vkim_playing_drawable_rect_count = 0x7f0b002d; // 2131427373
|
public static final int vkim_playing_drawable_rect_count = 0x7f0b002d; // 2131427373
|
||||||
|
public static final int applock__activity_lock_reenable_minutes = 0x7f0b002e; // 2131427374
|
||||||
|
public static final int applock__failure_retry_delay = 0x7f0b002f; // 2131427375
|
||||||
|
public static final int applock__input_pin_item_count = 0x7f0b0030; // 2131427376
|
||||||
|
public static final int applock__max_retry_count = 0x7f0b0031; // 2131427377
|
||||||
public static final int emoji_size_in_sprite = 0x7f0b0012; // 2131427346
|
public static final int emoji_size_in_sprite = 0x7f0b0012; // 2131427346
|
||||||
public static final int s1dip = 0x7f0b0027; // 2131427367
|
public static final int s1dip = 0x7f0b0027; // 2131427367
|
||||||
public static final int s2dip = 0x7f0b0028; // 2131427368
|
public static final int s2dip = 0x7f0b0028; // 2131427368
|
||||||
@@ -12883,6 +12923,11 @@ public final class R {
|
|||||||
public static final int wppreview = 0x7f0d06df; // 2131560159
|
public static final int wppreview = 0x7f0d06df; // 2131560159
|
||||||
public static final int write_bar_i = 0x7f0d06e0; // 2131560160
|
public static final int write_bar_i = 0x7f0d06e0; // 2131560160
|
||||||
public static final int color_picker_item = 0x7f0d06e1; // 2131560161
|
public static final int color_picker_item = 0x7f0d06e1; // 2131560161
|
||||||
|
public static final int applock__activity_lock_creation = 0x7f0d06e2; // 2131560162
|
||||||
|
public static final int applock__activity_unlock = 0x7f0d06e3; // 2131560163
|
||||||
|
public static final int applock__fake_edit_text = 0x7f0d06e4; // 2131560164
|
||||||
|
public static final int applock__lock_creation = 0x7f0d06e5; // 2131560165
|
||||||
|
public static final int applock__unlock = 0x7f0d06e6; // 2131560166
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class menu {
|
public static final class menu {
|
||||||
@@ -13025,7 +13070,7 @@ public final class R {
|
|||||||
public static final int ic_launcher_shine = 0x7f0f005d; // 2131689565
|
public static final int ic_launcher_shine = 0x7f0f005d; // 2131689565
|
||||||
public static final int ic_launcher_shine_background = 0x7f0f005e; // 2131689566
|
public static final int ic_launcher_shine_background = 0x7f0f005e; // 2131689566
|
||||||
public static final int ic_launcher_shine_foreground = 0x7f0f005f; // 2131689567
|
public static final int ic_launcher_shine_foreground = 0x7f0f005f; // 2131689567
|
||||||
public static final int vt_adaptive_launcher_foreground = 0x7f0f0043; // 2131689539
|
public static final int vt_adaptive_launcher_foneground = 0x7f0f0043; // 2131689539
|
||||||
public static final int vt_launcher_round = 0x7f0f0046; // 2131689542
|
public static final int vt_launcher_round = 0x7f0f0046; // 2131689542
|
||||||
public static final int ic_logo_facebook_28 = 0x7f0f0003; // 2131689475
|
public static final int ic_logo_facebook_28 = 0x7f0f0003; // 2131689475
|
||||||
public static final int ic_logo_google_28 = 0x7f0f0004; // 2131689476
|
public static final int ic_logo_google_28 = 0x7f0f0004; // 2131689476
|
||||||
@@ -19658,42 +19703,43 @@ public final class R {
|
|||||||
public static final int vkx_integration_enabled_info = 0x7f12188a; // 2131892362
|
public static final int vkx_integration_enabled_info = 0x7f12188a; // 2131892362
|
||||||
public static final int whitelisted_ad_groups_stories_title = 0x7f12188b; // 2131892363
|
public static final int whitelisted_ad_groups_stories_title = 0x7f12188b; // 2131892363
|
||||||
public static final int writebar_ios_title = 0x7f12188c; // 2131892364
|
public static final int writebar_ios_title = 0x7f12188c; // 2131892364
|
||||||
public static final int cache_size = 0x7f12188d; // 2131892365
|
public static final int applock__action_settings = 0x7f12188d; // 2131892365
|
||||||
public static final int cache_size_summ = 0x7f12188e; // 2131892366
|
public static final int applock__create_chooser_option_fingerprint = 0x7f12188e; // 2131892366
|
||||||
public static final int clear_all_cache = 0x7f12188f; // 2131892367
|
public static final int applock__create_chooser_option_pin = 0x7f12188f; // 2131892367
|
||||||
public static final int disable_roaming_state = 0x7f121890; // 2131892368
|
public static final int applock__description_chooser = 0x7f121890; // 2131892368
|
||||||
public static final int disable_roaming_state_submit = 0x7f121891; // 2131892369
|
public static final int applock__description_confirm = 0x7f121891; // 2131892369
|
||||||
public static final int invalidate_theme_cache_each_update_title = 0x7f121892; // 2131892370
|
public static final int applock__description_create_fingerprint = 0x7f121892; // 2131892370
|
||||||
public static final int invertFilters = 0x7f121893; // 2131892371
|
public static final int applock__description_create_fingerprint_paused = 0x7f121893; // 2131892371
|
||||||
public static final int invertFilters_summ = 0x7f121894; // 2131892372
|
public static final int applock__description_create_pin = 0x7f121894; // 2131892372
|
||||||
public static final int invertcached_summ = 0x7f121895; // 2131892373
|
public static final int applock__description_create_pin_reattempt = 0x7f121895; // 2131892373
|
||||||
public static final int invertcached_title = 0x7f121896; // 2131892374
|
public static final int applock__description_unlock_fingerprint = 0x7f121896; // 2131892374
|
||||||
public static final int microg_dialog1 = 0x7f121897; // 2131892375
|
public static final int applock__description_unlock_fingerprint_help = 0x7f121897; // 2131892375
|
||||||
public static final int microg_dialog2 = 0x7f121898; // 2131892376
|
public static final int applock__description_unlock_pin = 0x7f121898; // 2131892376
|
||||||
public static final int microg_dialog3 = 0x7f121899; // 2131892377
|
public static final int applock__empty_item_min_size_percent = 0x7f121899; // 2131892377
|
||||||
public static final int serverFeaturesDisable = 0x7f12189a; // 2131892378
|
public static final int applock__fingerprint_alias = 0x7f12189a; // 2131892378
|
||||||
public static final int serverFeaturesDisable_summ = 0x7f12189b; // 2131892379
|
public static final int applock__fingerprint_error_hardware = 0x7f12189b; // 2131892379
|
||||||
public static final int sett_autoupdates = 0x7f12189c; // 2131892380
|
public static final int applock__fingerprint_error_none = 0x7f12189c; // 2131892380
|
||||||
public static final int sett_autoupdates_summ = 0x7f12189d; // 2131892381
|
public static final int applock__fingerprint_error_not_enrolled = 0x7f12189d; // 2131892381
|
||||||
public static final int sett_battery = 0x7f12189e; // 2131892382
|
public static final int applock__fingerprint_error_permission = 0x7f12189e; // 2131892382
|
||||||
public static final int sett_battery_summ1 = 0x7f12189f; // 2131892383
|
public static final int applock__fingerprint_error_permission_multiple = 0x7f12189f; // 2131892383
|
||||||
public static final int sett_battery_summ2 = 0x7f1218a0; // 2131892384
|
public static final int applock__fingerprint_error_unknown = 0x7f1218a0; // 2131892384
|
||||||
public static final int sett_chats = 0x7f1218a1; // 2131892385
|
public static final int applock__fingerprint_error_unrecognized = 0x7f1218a1; // 2131892385
|
||||||
public static final int sett_chats_summ = 0x7f1218a2; // 2131892386
|
public static final int applock__password_char = 0x7f1218a2; // 2131892386
|
||||||
public static final int sett_community = 0x7f1218a3; // 2131892387
|
public static final int applock__toast_unlock_required = 0x7f1218a3; // 2131892387
|
||||||
public static final int sett_community_summ = 0x7f1218a4; // 2131892388
|
public static final int applock__unlock_error_insufficient_selection = 0x7f1218a4; // 2131892388
|
||||||
public static final int sett_missing_links = 0x7f1218a5; // 2131892389
|
public static final int applock__unlock_error_match_failed = 0x7f1218a5; // 2131892389
|
||||||
public static final int sett_missing_links_summ = 0x7f1218a6; // 2131892390
|
public static final int applock__unlock_error_no_matching_pin_found = 0x7f1218a6; // 2131892390
|
||||||
public static final int sett_nogms_summ = 0x7f1218a7; // 2131892391
|
public static final int applock__unlock_error_retry_limit_exceeded = 0x7f1218a7; // 2131892391
|
||||||
public static final int sett_notifs = 0x7f1218a8; // 2131892392
|
public static final int cache_size = 0x7f1218a8; // 2131892392
|
||||||
public static final int sett_notifs_summ = 0x7f1218a9; // 2131892393
|
public static final int cache_size_summ = 0x7f1218a9; // 2131892393
|
||||||
public static final int sett_recommendations = 0x7f1218aa; // 2131892394
|
public static final int clear_all_cache = 0x7f1218aa; // 2131892394
|
||||||
public static final int tgs_token_missing = 0x7f1218ab; // 2131892395
|
public static final int invalidate_theme_cache_each_update_title = 0x7f1218ab; // 2131892395
|
||||||
public static final int tgs_token_reset = 0x7f1218ac; // 2131892396
|
public static final int invertFilters = 0x7f1218ac; // 2131892396
|
||||||
public static final int tgs_token_reset_dialog = 0x7f1218ad; // 2131892397
|
public static final int invertFilters_summ = 0x7f1218ad; // 2131892397
|
||||||
public static final int tgs_token_reset_menu = 0x7f1218ae; // 2131892398
|
public static final int invertcached_summ = 0x7f1218ae; // 2131892398
|
||||||
public static final int update_err = 0x7f1218af; // 2131892399
|
public static final int invertcached_title = 0x7f1218af; // 2131892399
|
||||||
public static final int update_latest = 0x7f1218b0; // 2131892400
|
public static final int pin_not_set = 0x7f1218b0; // 2131892400
|
||||||
|
public static final int pin_reset = 0x7f1218b1; // 2131892401
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class style {
|
public static final class style {
|
||||||
@@ -20829,6 +20875,11 @@ public final class R {
|
|||||||
public static final int VkAmoledTheme = 0x7f130472; // 2131952754
|
public static final int VkAmoledTheme = 0x7f130472; // 2131952754
|
||||||
public static final int VkMilkAmoledStyle = 0x7f130473; // 2131952755
|
public static final int VkMilkAmoledStyle = 0x7f130473; // 2131952755
|
||||||
public static final int VkMilkAmoledTheme = 0x7f130474; // 2131952756
|
public static final int VkMilkAmoledTheme = 0x7f130474; // 2131952756
|
||||||
|
public static final int applock__action_settings = 0x7f130487; // 2131952775
|
||||||
|
public static final int applock__content_parent = 0x7f130488; // 2131952776
|
||||||
|
public static final int applock__description = 0x7f130489; // 2131952777
|
||||||
|
public static final int applock__input_fingerprint_view = 0x7f13048a; // 2131952778
|
||||||
|
public static final int applock__input_pin_view = 0x7f13048b; // 2131952779
|
||||||
public static final int Base_V23_Theme_AppCompat = 0x7f13009d; // 2131951773
|
public static final int Base_V23_Theme_AppCompat = 0x7f13009d; // 2131951773
|
||||||
public static final int Base_V23_Theme_AppCompat_Light = 0x7f13009e; // 2131951774
|
public static final int Base_V23_Theme_AppCompat_Light = 0x7f13009e; // 2131951774
|
||||||
public static final int Platform_V25_AppCompat = 0x7f13017f; // 2131951999
|
public static final int Platform_V25_AppCompat = 0x7f13017f; // 2131951999
|
||||||
|
|||||||
+10
-10
@@ -35,16 +35,16 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
|
|||||||
private final View mCounterContainer;
|
private final View mCounterContainer;
|
||||||
private final TextView mCounter;
|
private final TextView mCounter;
|
||||||
private final View mDot;
|
private final View mDot;
|
||||||
private final int mMargin;
|
|
||||||
private final float mSmallScale;
|
|
||||||
private final ImageView mIcon;
|
|
||||||
private final ImageView mActionIcon;
|
|
||||||
private int mItemPos;
|
private int mItemPos;
|
||||||
private MenuItemImpl mItemData;
|
private MenuItemImpl mItemData;
|
||||||
private ColorStateList mColorStateList;
|
private ColorStateList mColorStateList;
|
||||||
|
private final int mMargin;
|
||||||
|
private final float mSmallScale;
|
||||||
private boolean mShiftingModeEnabled;
|
private boolean mShiftingModeEnabled;
|
||||||
private boolean mIconsModeEnabled;
|
private boolean mIconsModeEnabled;
|
||||||
private boolean mStaticModeEnabled;
|
private boolean mStaticModeEnabled;
|
||||||
|
private final ImageView mIcon;
|
||||||
|
private final ImageView mActionIcon;
|
||||||
|
|
||||||
public BottomNavigationItemView(@NonNull Context context) {
|
public BottomNavigationItemView(@NonNull Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -69,10 +69,10 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
|
|||||||
this.mIcon.getLayoutParams().height = V.a(28.0F);
|
this.mIcon.getLayoutParams().height = V.a(28.0F);
|
||||||
|
|
||||||
this.mSmallLabel = this.findViewById(R.id.smallLabel);
|
this.mSmallLabel = this.findViewById(R.id.smallLabel);
|
||||||
mSmallLabel.setTextColor(getCSTDock(this.getContext()));
|
mSmallLabel.setTextColor(getCSTDock());
|
||||||
|
|
||||||
this.mLargeLabel = this.findViewById(R.id.largeLabel);
|
this.mLargeLabel = this.findViewById(R.id.largeLabel);
|
||||||
mLargeLabel.setTextColor(getCSTDock(this.getContext()));
|
mLargeLabel.setTextColor(getCSTDock());
|
||||||
|
|
||||||
LayoutInflater.from(this.getContext()).inflate(R.layout.navigation_bottom_counter, this, true);
|
LayoutInflater.from(this.getContext()).inflate(R.layout.navigation_bottom_counter, this, true);
|
||||||
this.mCounterContainer = this.findViewById(R.id.counter_container);
|
this.mCounterContainer = this.findViewById(R.id.counter_container);
|
||||||
@@ -101,10 +101,6 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
|
|||||||
return this.mItemPos;
|
return this.mItemPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItemPosition(int pos) {
|
|
||||||
this.mItemPos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
public void initialize(MenuItemImpl impl, int i) {
|
public void initialize(MenuItemImpl impl, int i) {
|
||||||
this.mItemData = impl;
|
this.mItemData = impl;
|
||||||
@@ -314,6 +310,10 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
|
|||||||
ViewCompat.setBackground(this, resId != 0 ? ContextCompat.getDrawable(this.getContext(), resId) : null);
|
ViewCompat.setBackground(this, resId != 0 ? ContextCompat.getDrawable(this.getContext(), resId) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setItemPosition(int pos) {
|
||||||
|
this.mItemPos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
public void setShiftingMode(boolean var1) {
|
public void setShiftingMode(boolean var1) {
|
||||||
this.mShiftingModeEnabled = var1;
|
this.mShiftingModeEnabled = var1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package ru.vtosters.hooks;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.lite.utils.newsfeed.NewsFeedFiltersUtils;
|
import ru.vtosters.lite.utils.NewsFeedFiltersUtils;
|
||||||
|
|
||||||
public class AdBlockHook {
|
public class AdBlockHook {
|
||||||
public static JSONObject discoverInject(JSONObject json) throws JSONException {
|
public static JSONObject discoverInject(JSONObject json) throws JSONException {
|
||||||
@@ -11,15 +11,11 @@ public class AdBlockHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static JSONArray feedInject(JSONArray items) {
|
public static JSONArray feedInject(JSONArray items) {
|
||||||
return NewsFeedFiltersUtils.feedInject(items, false);
|
return NewsFeedFiltersUtils.feedInject(items);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONArray wallGetById(JSONArray items) {
|
public static JSONObject storiesads(JSONObject json, boolean isDeleteFix) throws JSONException {
|
||||||
return NewsFeedFiltersUtils.feedInject(items, true);
|
return NewsFeedFiltersUtils.storiesads(json, isDeleteFix);
|
||||||
}
|
|
||||||
|
|
||||||
public static JSONObject storiesads(JSONObject json, boolean isDeleteFix) {
|
|
||||||
return NewsFeedFiltersUtils.storiesAds(json, isDeleteFix);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import com.vk.im.engine.models.messages.NestedMsg;
|
|||||||
import com.vk.libsqliteext.CustomSqliteExtensionsKt;
|
import com.vk.libsqliteext.CustomSqliteExtensionsKt;
|
||||||
import io.requery.android.database.sqlite.SQLiteDatabase;
|
import io.requery.android.database.sqlite.SQLiteDatabase;
|
||||||
import io.requery.android.database.sqlite.SQLiteOpenHelper;
|
import io.requery.android.database.sqlite.SQLiteOpenHelper;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||||
|
import static ru.vtosters.hooks.other.Preferences.getString;
|
||||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||||
|
|
||||||
public class DeletedMessagesHook {
|
public class DeletedMessagesHook {
|
||||||
@@ -69,7 +69,7 @@ public class DeletedMessagesHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String getPrefixUndelete() {
|
private static String getPrefixUndelete() {
|
||||||
var prefs = Preferences.getPreferences().getString("undeletemsg_prefix_value", "🗑");
|
var prefs = getString("undeletemsg_prefix_value");
|
||||||
|
|
||||||
if (prefs.isEmpty()) {
|
if (prefs.isEmpty()) {
|
||||||
return "\uD83D\uDDD1 ";
|
return "\uD83D\uDDD1 ";
|
||||||
@@ -78,7 +78,7 @@ public class DeletedMessagesHook {
|
|||||||
return prefs + " ";
|
return prefs + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateDialog(MsgDeleteLpTask msgDeleteLpTask) {
|
public static void updateDialog(MsgDeleteLpTask msgDeleteLpTask) throws NoSuchFieldException, IllegalAccessException {
|
||||||
var cursor = getMessageFromDatabaseById(msgDeleteLpTask.d);
|
var cursor = getMessageFromDatabaseById(msgDeleteLpTask.d);
|
||||||
if (cursor == null) return;
|
if (cursor == null) return;
|
||||||
|
|
||||||
@@ -157,4 +157,4 @@ public class DeletedMessagesHook {
|
|||||||
return msgIdsList;
|
return msgIdsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class DockBarInjector {
|
|||||||
for (DockBarTab tab : sManager.getSelectedTabs()) {
|
for (DockBarTab tab : sManager.getSelectedTabs()) {
|
||||||
MenuItem add = menu.add(0, tab.id, 0, tab.titleID);
|
MenuItem add = menu.add(0, tab.id, 0, tab.titleID);
|
||||||
|
|
||||||
add.setIcon(new RecoloredDrawable(getResources().getDrawable(tab.iconID), getCSTDock(navigationView.getContext())));
|
add.setIcon(new RecoloredDrawable(getResources().getDrawable(tab.iconID), getCSTDock()));
|
||||||
add.setTitle(getGlobalContext().getString(tab.titleID));
|
add.setTitle(getGlobalContext().getString(tab.titleID));
|
||||||
add.setCheckable(true);
|
add.setCheckable(true);
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ public class DockBarInjector {
|
|||||||
item.setStaticMode(true);
|
item.setStaticMode(true);
|
||||||
item.setIconsMode(false);
|
item.setIconsMode(false);
|
||||||
item.setShiftingMode(false);
|
item.setShiftingMode(false);
|
||||||
item.setTextColor(getCSTDock(item.getContext()));
|
item.setTextColor(getCSTDock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,9 +72,8 @@ public class EncryptionMessagesHook {
|
|||||||
public static String decryptMessage(String msgBody, int peer) {
|
public static String decryptMessage(String msgBody, int peer) {
|
||||||
try {
|
try {
|
||||||
for (IMProcessor processor : EncryptProvider.processors) {
|
for (IMProcessor processor : EncryptProvider.processors) {
|
||||||
if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || EncryptProvider.getKeyForProcessor(processor, peer) != null)) {
|
if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || EncryptProvider.getKeyForProcessor(processor, peer) != null))
|
||||||
return "\uD83D\uDD12 " + processor.getUIName() + ": " + injectOwnTextAll(processor.decode(msgBody, EncryptProvider.getKeyForProcessor(processor, peer)));
|
return "\uD83D\uDD12 " + injectOwnTextAll(processor.decode(msgBody, EncryptProvider.getKeyForProcessor(processor, peer)));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package ru.vtosters.hooks;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
public class FirebaseGzipSupport {
|
|
||||||
public static JSONObject getJsonObjectFromConnection(URLConnection connection) throws IOException, JSONException {
|
|
||||||
InputStream inputStream = getDecompressedInputStream(connection);
|
|
||||||
String jsonString = readInputStream(inputStream);
|
|
||||||
return new JSONObject(jsonString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InputStream getDecompressedInputStream(URLConnection connection) throws IOException {
|
|
||||||
String contentEncoding = connection.getHeaderField("Content-Encoding");
|
|
||||||
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
|
|
||||||
return new GZIPInputStream(connection.getInputStream());
|
|
||||||
}
|
|
||||||
return connection.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readInputStream(InputStream inputStream) throws IOException {
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int read;
|
|
||||||
while ((read = bufferedReader.read()) != -1) {
|
|
||||||
sb.append((char) read);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package ru.vtosters.hooks;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.vk.dto.gift.GiftCategory;
|
|
||||||
import com.vtosters.lite.TabletDialogActivity;
|
|
||||||
import com.vtosters.lite.fragments.gifts.GiftCategoryFragment;
|
|
||||||
import ru.vtosters.lite.ui.fragments.SuperAppEditorFragment;
|
|
||||||
import ru.vtosters.lite.utils.NavigatorUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class GiftsFix {
|
|
||||||
public static Bundle staticBundle;
|
|
||||||
|
|
||||||
public static void openGiftCategoryFragment(Activity activity, ArrayList<Integer> userIds, GiftCategory giftCategory, int balance, String reference) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putIntegerArrayList("uids", userIds);
|
|
||||||
bundle.putParcelable("data", giftCategory);
|
|
||||||
bundle.putInt("balance", balance);
|
|
||||||
bundle.putString("ref", reference);
|
|
||||||
|
|
||||||
staticBundle = bundle;
|
|
||||||
|
|
||||||
NavigatorUtils.switchFragment(activity, GiftCategoryFragment.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bundle getBundle() {
|
|
||||||
return staticBundle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package ru.vtosters.hooks;
|
|
||||||
|
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
|
|
||||||
public class GroupCoverHook {
|
|
||||||
public static boolean needToDisableCovers() {
|
|
||||||
return Preferences.getBoolValue("disablevideoscovers", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user