12 Commits

Author SHA1 Message Date
苇子 56605d7069 Merge pull request #37 from srdr0p/master
Support Wechat 7.0
2020-09-15 14:02:52 +08:00
LittleDrop 8cbbb38371 Support Android 10 2020-05-09 10:46:59 +08:00
LittleDrop 8d7847bc61 Update gitignore 2020-05-09 10:19:52 +08:00
LittleDrop 2084746f38 Update gradle etc 2020-05-08 21:55:03 +08:00
srdr0p 618baa0c52 Support Wechat 7.0 2019-12-16 18:57:59 +08:00
苇子 31a189ca20 Merge pull request #32 from sparcat/master
fixes can't write step.info on 8.0+
2018-12-25 10:53:24 +08:00
sparcat 2165ef3abf fixes can't write step.info on 8.0+ 2018-12-23 13:22:02 +08:00
苇子 60164cdf4b Merge pull request #16 from ttimasdf/master
Fix #15, Alipay step manipulation caused by permission problems
2018-02-09 20:10:21 +08:00
ttimasdf 3a328f64aa Fix #15, Alipay step manipulation caused by permission problems
Also fix a bug that "load" button for Alipay not loading changed data
2018-02-09 11:21:29 +08:00
iweizime 5b63caf77d update build.gradle 2017-11-22 00:51:29 +08:00
iweizime 3d530ac18a add support to QQ 7.2.5 2017-11-22 00:50:25 +08:00
iweizime 4fe016832c add image for tencent tea 2017-09-13 00:11:52 +08:00
17 changed files with 564 additions and 129 deletions
+84 -5
View File
@@ -1,6 +1,85 @@
# Created by .ignore support plugin (hsz.mobi)
### Android template
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/build
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
+3
View File
@@ -3,6 +3,9 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<compositeConfiguration>
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
+14 -16
View File
@@ -1,43 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<list size="10">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="4" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<list size="9">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
+17 -22
View File
@@ -1,15 +1,16 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
compileSdkVersion 27
buildToolsVersion '29.0.1'
defaultConfig {
applicationId "me.iweizi.stepchanger"
minSdkVersion 21
targetSdkVersion 26
targetSdkVersion 27
versionCode 1
versionName '1.3'
versionName '1.4'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
setProperty("archivesBaseName", "StepChanger")
}
buildTypes {
release {
@@ -17,29 +18,23 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
"StepChanger-${variant.versionName}-${variant.buildType.name}.apk")
}
}
productFlavors {
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation ('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
compile 'com.android.support:support-v4:26.0.0-alpha1'
compile 'com.android.support:design:26.0.0-alpha1'
compile 'eu.chainfire:libsuperuser:1.0.0.201704021214'
compile 'com.alibaba:fastjson:1.1.63.android'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
compile 'com.android.support:cardview-v7:26.0.0-alpha1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'eu.chainfire:libsuperuser:1.1.0.+'
implementation 'com.alibaba:fastjson:1.1.63.android'
implementation'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
implementation'com.android.support:cardview-v7:27.1.1'
}
+1
View File
@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<application
android:name="me.iweizi.stepchanger.MyApplication"
android:allowBackup="false"
android:icon="@mipmap/running"
android:label="@string/app_name"
@@ -0,0 +1,18 @@
package me.iweizi.stepchanger;
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
private static Context ctx = null;
@Override
public void onCreate() {
super.onCreate();
ctx = this;
}
public static Context getContext() {
return ctx;
}
}
@@ -6,6 +6,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import eu.chainfire.libsuperuser.Shell;
import me.iweizi.stepchanger.utils.Utils;
@@ -230,7 +231,11 @@ public class StepData {
isRead = booleans[0];
mSuAvailable = Shell.SU.available();
if (mSuAvailable) {
Shell.SU.run(ROOT_CMD);
try {
Shell.Pool.SU.run(ROOT_CMD);
} catch (Shell.ShellDiedException e) {
Log.e("StepChanger", e.toString());
}
}
return null;
}
@@ -8,6 +8,7 @@ import android.content.SharedPreferences;
import com.alibaba.fastjson.JSON;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -39,8 +40,8 @@ public class NewPedoMeter extends StepData {
private final String mNewPedometerPrivate;
private final String mAlipayNewPedometer;
private final String mAlipayNewPedometerPrivate;
private final File mAlipayNewPedometerFile;
private final File mAlipayNewPedometerPrivateFile;
private final File mNewPedometerFile;
private final File mNewPedometerPrivateFile;
private SharedPreferences mNewPedometerSP;
private SharedPreferences mNewPedometerPrivateSP;
@@ -51,8 +52,8 @@ public class NewPedoMeter extends StepData {
mAlipayNewPedometer = ALIPAY_SP_DIR + NEW_PEDOMETER;
mAlipayNewPedometerPrivate = ALIPAY_SP_DIR + NEW_PEDOMETER_PRIVATE;
mAlipayNewPedometerFile = new File(mAlipayNewPedometer);
mAlipayNewPedometerPrivateFile = new File(mAlipayNewPedometerPrivate);
mNewPedometerFile = new File(mNewPedometer);
mNewPedometerPrivateFile = new File(mNewPedometerPrivate);
File sp_dir = new File(SP_DIR);
if (!sp_dir.exists()) {
@@ -60,9 +61,16 @@ public class NewPedoMeter extends StepData {
sp_dir.mkdir();
}
try {
if (!mNewPedometerFile.exists()) mNewPedometerFile.createNewFile();
if (!mNewPedometerPrivateFile.exists()) mNewPedometerPrivateFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
ROOT_CMD = new String[]{
"chmod o+rw " + mAlipayNewPedometer,
"chmod o+rw " + mAlipayNewPedometerPrivate,
"chmod g+rw " + mNewPedometer,
"chmod g+rw " + mNewPedometerPrivate,
};
mLoadButtonId = R.id.alipay_load_button;
@@ -76,12 +84,12 @@ public class NewPedoMeter extends StepData {
@Override
public int read(Context context) {
killAlipayProcess(context);
Shell.SH.run(new String[]{
"cp " + mAlipayNewPedometerPrivate + " " + mNewPedometerPrivate,
"cp " + mAlipayNewPedometer + " " + mNewPedometer
Shell.SU.run(new String[]{
"cat " + mAlipayNewPedometerPrivate + " > " + mNewPedometerPrivate,
"cat " + mAlipayNewPedometer + " > " + mNewPedometer
});
mNewPedometerSP = context.getSharedPreferences("NewPedoMeter", Context.MODE_PRIVATE);
mNewPedometerPrivateSP = context.getSharedPreferences("NewPedoMeter_private", Context.MODE_PRIVATE);
mNewPedometerSP = context.getSharedPreferences("NewPedoMeter", Context.MODE_MULTI_PROCESS);
mNewPedometerPrivateSP = context.getSharedPreferences("NewPedoMeter_private", Context.MODE_MULTI_PROCESS);
return SUCCESS;
}
@@ -102,7 +110,7 @@ public class NewPedoMeter extends StepData {
newLastRecord.setTime(lastUploadTime);
newStepRecord.add(newLastRecord);
saveStepRecord(newStepRecord);
Shell.SH.run(new String[]{
Shell.SU.run(new String[]{
"cat " + mNewPedometerPrivate + " > " + mAlipayNewPedometerPrivate
});
return SUCCESS;
@@ -160,12 +168,12 @@ public class NewPedoMeter extends StepData {
@Override
protected boolean canRead() {
return mAlipayNewPedometerFile.canRead() && mAlipayNewPedometerPrivateFile.canRead();
return mNewPedometerFile.canRead() && mNewPedometerPrivateFile.canRead();
}
@Override
protected boolean canWrite() {
return mAlipayNewPedometerFile.canWrite() && mAlipayNewPedometerPrivateFile.canWrite();
return mNewPedometerFile.canWrite() && mNewPedometerPrivateFile.canWrite();
}
@@ -10,7 +10,7 @@ public class QQDetailsActivity extends BaseDetailsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
mLayoutId = R.layout.activity_qq_details;
mStepData = StepInfo.get();
mStepData = StepInfo.get(this);
super.onCreate(savedInstanceState);
}
}
@@ -10,7 +10,6 @@ public class QQFragment extends BaseFragment {
public QQFragment() {
mLayoutId = R.layout.fragment_qq;
mStepData = StepInfo.get();
mDetailsClass = QQDetailsActivity.class;
}
@@ -23,6 +22,7 @@ public class QQFragment extends BaseFragment {
@Override
protected void findView(View view) {
mStepData = StepInfo.get(getActivity());
mLoadButton = view.findViewById(R.id.qq_load_button);
mStoreButton = view.findViewById(R.id.qq_store_button);
mDetailsButton = view.findViewById(R.id.qq_details_button);
@@ -2,14 +2,25 @@ package me.iweizi.stepchanger.qq;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.util.Enumeration;
import eu.chainfire.libsuperuser.Shell;
import me.iweizi.stepchanger.R;
import me.iweizi.stepchanger.StepData;
import me.iweizi.stepchanger.utils.Utils;
@@ -21,27 +32,58 @@ import me.iweizi.stepchanger.utils.Utils;
public class StepInfo extends StepData {
private static final StepInfo sStepInfo = new StepInfo();
private static final String STEP_INFO = "/data/data/com.tencent.mobileqq/files/step.info";
private static StepInfo sStepInfo = null;
private static final String STEP_INFO = "/data/data/me.iweizi.stepchanger/files/step.info";
private static final String STEP_SIGN_INFO = "/data/data/me.iweizi.stepchanger/files/stepSign.info";
private static final String ORIG_STEP_INFO = "/data/data/com.tencent.mobileqq/files/step.info";
private static final String ORIG_STEP_SIGN_INFO = "/data/data/com.tencent.mobileqq/files/stepSign.info";
private static final String sKey = "4eY#X@~g.+U)2%$<";
private static final String QQ = "com.tencent.mobileqq";
private static final String QQ_MSF = "com.tencent.mobileqq:MSF";
private static final String KEYSTORE_DIR = "/data/misc/keystore/user_0/";
private static final int MAX_SUPPORT_VERSION_CODE = 730;
private static final File sStepInfoFile = new File(STEP_INFO);
private static final File sStepSignInfoFile = new File(STEP_SIGN_INFO);
private JSONObject mStepInfo;
private Cryptor mCryptor;
private StepInfo() {
ROOT_CMD = new String[]{
"chmod o+rw " + STEP_INFO
};
private StepInfo(Context context) throws IOException {
File dir = context.getFilesDir();
if (!dir.exists())
dir.mkdirs();
if (!sStepInfoFile.exists())
sStepInfoFile.createNewFile();
if (!checkVersion(context)) {
if (!sStepSignInfoFile.exists())
sStepSignInfoFile.createNewFile();
/* 复制key */
int qq_uid = getUID("com.tencent.mobileqq", context);
int my_uid = getUID("me.iweizi.stepchanger", context);
ROOT_CMD = new String[]{
"cp " + KEYSTORE_DIR + String.valueOf(qq_uid) + "_USRCERT_step_info "
+ KEYSTORE_DIR + String.valueOf(my_uid) + "_USRCERT_step_info",
"cp " + KEYSTORE_DIR + String.valueOf(qq_uid) + "_USRPKEY_step_info "
+ KEYSTORE_DIR + String.valueOf(my_uid) + "_USRPKEY_step_info",
"chown keystore:keystore " + KEYSTORE_DIR + String.valueOf(my_uid) + "_USRCERT_step_info",
"chown keystore:keystore " + KEYSTORE_DIR + String.valueOf(my_uid) + "_USRPKEY_step_info"
};
}
mCryptor = new Cryptor(sKey.getBytes());
mStepInfo = null;
mLoadButtonId = R.id.qq_load_button;
mStoreButtonId = R.id.qq_store_button;
}
public static StepInfo get() {
public static StepInfo get(Context context) {
if (sStepInfo == null) {
try {
sStepInfo = new StepInfo(context);
} catch (Throwable e) {
return null;
}
}
return sStepInfo;
}
@@ -62,11 +104,23 @@ public class StepInfo extends StepData {
byte[] ciphertext;
byte[] plaintext;
try {
raf = new RandomAccessFile(STEP_INFO, "r");
ciphertext = new byte[(int) raf.length()];
raf.read(ciphertext);
plaintext = mCryptor.decrypt(ciphertext);
mStepInfo = new JSONObject(new String(plaintext));
Shell.SU.run(new String[]{
"cat " + ORIG_STEP_INFO + " > " + STEP_INFO
});
if(checkVersion(context)) {
raf = new RandomAccessFile(STEP_INFO, "r");
ciphertext = new byte[(int) raf.length()];
raf.read(ciphertext);
plaintext = mCryptor.decrypt(ciphertext);
mStepInfo = new JSONObject(new String(plaintext));
raf.close();
} else {
raf = new RandomAccessFile(STEP_INFO, "r");
plaintext = new byte[(int) raf.length()];
raf.read(plaintext);
mStepInfo = new JSONObject(new String(plaintext));
raf.close();
}
} catch (Throwable e) {
return FAIL;
}
@@ -78,12 +132,36 @@ public class StepInfo extends StepData {
protected int write(Context context) {
killQQProcess(context);
try {
FileOutputStream outputStream = new FileOutputStream(STEP_INFO);
String offsetKey = String.valueOf(Utils.beginOfToday()) + "_offset";
int offset = mStepInfo.getInt(offsetKey);
offset = offset + (int) (getStep() - getLastUploadStep());
mStepInfo.put(offsetKey, offset);
outputStream.write(mCryptor.encrypt(mStepInfo.toString().getBytes()));
if (checkVersion(context)) {
FileOutputStream outputStream = new FileOutputStream(STEP_INFO);
String offsetKey = String.valueOf(Utils.beginOfToday()) + "_offset";
int offset = mStepInfo.getInt(offsetKey);
offset = offset + (int) (getStep() - getLastUploadStep());
mStepInfo.put(offsetKey, offset);
outputStream.write(mCryptor.encrypt(mStepInfo.toString().getBytes()));
outputStream.close();
} else {
FileOutputStream stepOutputStream = new FileOutputStream(STEP_INFO);
String offsetKey = String.valueOf(Utils.beginOfToday()) + "_offset";
int offset = mStepInfo.getInt(offsetKey);
offset = offset + (int) (getStep() - getLastUploadStep());
mStepInfo.put(offsetKey, offset);
stepOutputStream.write(mStepInfo.toString().getBytes());
stepOutputStream.close();
FileOutputStream stepSignOutputStream = new FileOutputStream(STEP_SIGN_INFO);
String base64 = Base64.encodeToString(getSign(mStepInfo.toString().getBytes()), 0);
stepSignOutputStream.write(base64.getBytes());
stepSignOutputStream.close();
Shell.SU.run(new String[]{
"cat " + STEP_SIGN_INFO + " > " + ORIG_STEP_SIGN_INFO
});
}
Shell.SU.run(new String[]{
"cat " + STEP_INFO + " > " + ORIG_STEP_INFO
});
} catch (Throwable e) {
return FAIL;
}
@@ -107,6 +185,14 @@ public class StepInfo extends StepData {
String beginOfToday = String.valueOf(Utils.beginOfToday());
if (mStepInfo != null) {
try {
if (!mStepInfo.has(beginOfToday+"_total")) {
String beginOfYesterday = String.valueOf(Utils.beginOfToday() - 24 * 60 * 60 * 1000);
int yesterday_total = mStepInfo.getInt(beginOfYesterday + "_total");
mStepInfo.put(beginOfToday + "_total", yesterday_total);
mStepInfo.put(beginOfToday + "_init", yesterday_total);
mStepInfo.put(beginOfToday + "_offset", 0);
}
return (mStepInfo.getInt(beginOfToday + "_total")
- mStepInfo.getInt(beginOfToday + "_init")
+ mStepInfo.getInt(beginOfToday + "_offset"));
@@ -146,4 +232,51 @@ public class StepInfo extends StepData {
am.killBackgroundProcesses(QQ);
am.killBackgroundProcesses(QQ_MSF);
}
private boolean checkVersion(Context context) {
PackageInfo pi = null;
PackageManager pm = context.getPackageManager();
try {
pi = pm.getPackageInfo(QQ, 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return pi.versionCode <= MAX_SUPPORT_VERSION_CODE;
}
private int getUID(String packageName, Context context) {
ApplicationInfo ai = null;
PackageManager pm = context.getPackageManager();
try {
ai = pm.getApplicationInfo(packageName, 0);
return ai.uid;
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
private byte[] getSign(byte[] data) {
try {
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
KeyStore.Entry entry = ks.getEntry("step_info", null);
if (entry != null) {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(((KeyStore.PrivateKeyEntry)entry).getPrivateKey());
sig.update(data);
return sig.sign();
}
return null;
} catch (KeyStoreException e) {
return null;
} catch (CertificateException e) {
return null;
} catch (Exception e) {
return null;
}
}
}
@@ -0,0 +1,134 @@
package me.iweizi.stepchanger.wechat;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Locale;
abstract class StepBean {
public long beginOfToday;
public long lastSaveStepTime;
public long lastWriteTime;
public long sensorTimeStamp;
public long preSensorStep;
public long todayStep;
public long lastSaveSensorStep;
protected HashMap<Integer, Object> mStepCounterMap;
protected File mCfgFile;
protected StepBean(File cfgFile) {
mCfgFile = cfgFile;
}
public void read() throws IOException, ClassCastException , ClassNotFoundException{
FileInputStream fis = new FileInputStream(mCfgFile);
ObjectInputStream ois = new ObjectInputStream(fis);
// noinspection unchecked
mStepCounterMap = (HashMap<Integer, Object>) ois.readObject();
ois.close();
fis.close();
readConfig();
}
public void write(long step) throws IOException {
// push
FileOutputStream fos;
ObjectOutputStream oos;
fos = new FileOutputStream(mCfgFile);
oos = new ObjectOutputStream(fos);
writeStep(step);
oos.writeObject(mStepCounterMap);
oos.close();
fos.close();
}
abstract void readConfig();
abstract void writeStep(long step);
}
class StepBeanLegacy extends StepBean {
public StepBeanLegacy(File cfgFile) {
super(cfgFile);
}
@Override
protected void readConfig() {
this.beginOfToday = (Long) mStepCounterMap.get(202) * 10000L;
this.todayStep = (Long) mStepCounterMap.get(201);
this.preSensorStep = (Long) mStepCounterMap.get(203);
this.lastSaveStepTime = (Long) mStepCounterMap.get(204);
this.sensorTimeStamp = (Long) mStepCounterMap.get(209);
this.lastSaveSensorStep = this.todayStep;
this.lastWriteTime = this.lastSaveStepTime;
}
@Override
void writeStep(long step) {
mStepCounterMap.put(201, step);
}
}
class StepBeanV7 extends StepBean {
public StepBeanV7(File cfgFile) {
super(cfgFile);
}
@Override
protected void readConfig() {
String stringVec = (String) mStepCounterMap.get(301);
long[] lv = stringVec2long(stringVec);
this.beginOfToday = lv[0];
this.lastSaveStepTime = lv[1];
this.lastWriteTime = lv[2];
this.sensorTimeStamp = lv[3];
this.preSensorStep = lv[4];
this.todayStep = lv[5];
this.lastSaveSensorStep = lv[6];
}
private long[] stringVec2long(String data) {
if (data == null || data.isEmpty()) {
return new long[7];
}
String[] sv = data.split(",");
long[] lv = new long[sv.length];
for (int i = 0; i < lv.length; ++i) {
String s = sv[i];
long l;
if (s != null && !s.isEmpty()) {
l = Long.valueOf(s);
} else {
l = 0;
}
lv[i] = l;
}
return lv;
}
protected void writeStep(long step) {
this.todayStep = step;
String data = String.format(Locale.ENGLISH, "%d,%d,%d,%d,%d,%d,%d",
this.beginOfToday,
this.lastSaveStepTime,
this.lastWriteTime,
this.sensorTimeStamp,
this.preSensorStep,
this.todayStep,
this.lastSaveSensorStep);
mStepCounterMap.put(301, data);
}
}
@@ -2,15 +2,19 @@ package me.iweizi.stepchanger.wechat;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import eu.chainfire.libsuperuser.Shell;
import me.iweizi.stepchanger.MyApplication;
import me.iweizi.stepchanger.R;
import me.iweizi.stepchanger.StepData;
@@ -31,54 +35,103 @@ class StepCounterCfg extends StepData {
private static final int LAST_UPLOAD_TIME = 3;
private static final int LAST_UPLOAD_STEP = 4;
private static final File WX_DATA_DIR = new File("/data/data/com.tencent.mm/MicroMsg/");
@SuppressLint("SdCardPath")
private static final String STEP_COUNTER_CFG = "/data/data/com.tencent.mm/MicroMsg/stepcounter.cfg";
private static final String UPLOAD_STATUS_CFG = "MM_stepcounter.cfg";
@SuppressLint("SdCardPath")
private static final String MM_STEP_COUNTER_CFG = "/data/data/com.tencent.mm/MicroMsg/MM_stepcounter.cfg";
private static final String V7_LOCAL_STEP_CFG = "PUSH_stepcounter.cfg";
@SuppressLint("SdCardPath")
private static final String LEGACY_LOCAL_STEP_CFG = "stepcounter.cfg";
private static final String WECHAT = "com.tencent.mm";
private static final String WECHAT_EX = "com.tencent.mm:exdevice";
private static final StepCounterCfg sStepCounterCfg = new StepCounterCfg();
private final File mStepCounterCfgFile;
private final File mMMStepCounterCfgFile;
private HashMap<Integer, Long> mStepCounterMap = null;
private static StepCounterCfg sStepCounterCfg = null;
private HashMap<Integer, ?> mMMStepCounterMap = null;
private StepBean mStepBean; // must sync with mod_step_cfg
private File wx_local_step_cfg;
private File mod_step_cfg;
private File wx_upload_status_cfg;
private File mod_upload_status_cfg;
private StepCounterCfg() {
super();
mStepCounterCfgFile = new File(STEP_COUNTER_CFG);
mMMStepCounterCfgFile = new File(MM_STEP_COUNTER_CFG);
ROOT_CMD = new String[]{
"chmod o+rw " + mStepCounterCfgFile.getAbsolutePath(),
"chmod o+rw " + mMMStepCounterCfgFile.getAbsolutePath(),
"chmod o+x " + mStepCounterCfgFile.getParent(),
"chmod o+x " + mMMStepCounterCfgFile.getParent()
};
mLoadButtonId = R.id.wechat_load_button;
mStoreButtonId = R.id.wechat_store_button;
Context ctx = MyApplication.getContext();
int versionCode = getWeixinVersionCode();
wx_upload_status_cfg = new File(WX_DATA_DIR, UPLOAD_STATUS_CFG);
mod_upload_status_cfg = new File(ctx.getFilesDir(), UPLOAD_STATUS_CFG);
if (versionCode == -1) {
ROOT_CMD = new String[]{};
Toast.makeText(ctx, "Weixin is not installed.", Toast.LENGTH_SHORT).show();
wx_local_step_cfg = null;
mod_step_cfg = null;
mStepBean = null;
} else if (versionCode >= 1363) {
wx_local_step_cfg = new File(WX_DATA_DIR, V7_LOCAL_STEP_CFG);
mod_step_cfg = new File(ctx.getFilesDir(), V7_LOCAL_STEP_CFG);
mStepBean = new StepBeanV7(mod_step_cfg);
} else {
wx_local_step_cfg = new File(WX_DATA_DIR, LEGACY_LOCAL_STEP_CFG);
mod_step_cfg = new File(ctx.getFilesDir(), LEGACY_LOCAL_STEP_CFG);
mStepBean = new StepBeanLegacy(mod_step_cfg);
}
if (mod_step_cfg != null && mod_step_cfg.exists()) {
mod_step_cfg.delete();
}
if (mod_upload_status_cfg.exists()) {
mod_upload_status_cfg.delete();
}
ROOT_CMD = new String[]{
String.format("cp %s %s", wx_local_step_cfg, mod_step_cfg),
"chmod o+w " + mod_step_cfg,
String.format("cp %s %s", wx_upload_status_cfg, mod_upload_status_cfg),
"chmod o+w " + mod_upload_status_cfg,
};
}
private int getWeixinVersionCode() {
Context ctx = MyApplication.getContext();
PackageManager pm = ctx.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo("com.tencent.mm", 0);
return pi.versionCode;
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
static StepCounterCfg get() {
if (sStepCounterCfg == null) {
sStepCounterCfg = new StepCounterCfg();
}
return sStepCounterCfg;
}
@Override
protected int read(Context context) {
if (mStepBean == null) {
return FAIL;
}
FileInputStream fis;
ObjectInputStream ois;
killWechatProcess(context);
try {
Shell.Pool.SU.run(ROOT_CMD);
mStepBean.read();
fis = new FileInputStream(mStepCounterCfgFile);
ois = new ObjectInputStream(fis);
//noinspection unchecked
mStepCounterMap = (HashMap<Integer, Long>) ois.readObject();
ois.close();
fis.close();
fis = new FileInputStream(mMMStepCounterCfgFile);
fis = new FileInputStream(mod_upload_status_cfg);
ois = new ObjectInputStream(fis);
//noinspection unchecked
mMMStepCounterMap = (HashMap<Integer, ?>) ois.readObject();
@@ -91,20 +144,17 @@ class StepCounterCfg extends StepData {
@Override
protected int write(Context context) {
FileOutputStream fos;
ObjectOutputStream oos;
if (mStepCounterMap == null) {
if (mStepBean == null) {
return FAIL;
}
try {
killWechatProcess(context);
fos = new FileOutputStream(STEP_COUNTER_CFG);
oos = new ObjectOutputStream(fos);
mStepCounterMap.put(CURRENT_TODAY_STEP, getStep());
oos.writeObject(mStepCounterMap);
oos.close();
fos.close();
mStepBean.write(getStep());
String[] restore = new String[]{
String.format("cp %s %s", mod_step_cfg, wx_local_step_cfg),
String.format("cp %s %s", mod_upload_status_cfg, wx_upload_status_cfg),
};
Shell.Pool.SU.run(restore);
return SUCCESS;
} catch (Exception e) {
return FAIL;
@@ -113,12 +163,12 @@ class StepCounterCfg extends StepData {
@Override
protected boolean canRead() {
return mStepCounterCfgFile.canRead() && mMMStepCounterCfgFile.canRead();
return mod_upload_status_cfg.canRead() && (mod_step_cfg != null && mod_step_cfg.canRead());
}
@Override
protected boolean canWrite() {
return mStepCounterCfgFile.canWrite() && mMMStepCounterCfgFile.canWrite();
return mod_upload_status_cfg.canWrite() && (mod_step_cfg != null && mod_step_cfg.canWrite());
}
@Override
@@ -143,8 +193,8 @@ class StepCounterCfg extends StepData {
@Override
public long getLastSaveTime() {
if (mStepCounterMap != null) {
return mStepCounterMap.get(LAST_SAVE_STEP_TIME);
if (mStepBean != null) {
return mStepBean.lastSaveStepTime;
} else {
return -1;
}
@@ -152,8 +202,9 @@ class StepCounterCfg extends StepData {
@Override
public long getLastSaveSensorStep() {
if (mStepCounterMap != null) {
return mStepCounterMap.get(PRE_SENSOR_STEP);
if (mStepBean != null) {
return mStepBean.preSensorStep;
} else {
return -1;
}
@@ -161,7 +212,7 @@ class StepCounterCfg extends StepData {
@Override
public boolean isLoaded() {
return mMMStepCounterMap != null && mStepCounterMap != null;
return mMMStepCounterMap != null && mStepBean != null;
}
private void killWechatProcess(Context context) {
@@ -170,4 +221,6 @@ class StepCounterCfg extends StepData {
am.killBackgroundProcesses(WECHAT);
am.killBackgroundProcesses(WECHAT_EX);
}
}
+3 -1
View File
@@ -2,10 +2,11 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.3.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -14,6 +15,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
}
}
+11 -5
View File
@@ -1,13 +1,19 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
## Project-wide Gradle settings.
#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Mon Sep 25 18:31:59 CST 2017
systemProp.http.proxyHost=127.0.0.1
systemProp.http.nonProxyHosts=192.168.*
org.gradle.jvmargs=-Xmx1536m
systemProp.http.proxyPort=1080
+2 -2
View File
@@ -1,6 +1,6 @@
#Mon Sep 04 18:39:38 CST 2017
#Fri May 08 20:32:03 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB