diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd45b12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.iml +.gradle +/local.properties +/.idea/caches/build_file_checksums.ser +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..30aa626 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 0000000..57927c5 --- /dev/null +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eb4e391 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..89e996d --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,43 @@ +apply plugin: 'com.android.application' + +android { + signingConfigs { + config { + keyAlias 'hongbao' + keyPassword '123456' + storeFile file('hongbao.jks') + storePassword '123456' + } + } + compileSdkVersion 28 + defaultConfig { + applicationId "group.tonight.hongbao" + minSdkVersion 18 + targetSdkVersion 28 + versionCode 1 + versionName "1.0.0 alpha-1" + signingConfig signingConfigs.config + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.config + } + debug { + signingConfig signingConfigs.config + } + } + productFlavors { + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.github.zhaokaiqiang.klog:library:1.6.0' + //必须使用 + implementation 'com.lzy.net:okgo:3.0.4' + implementation 'com.github.bumptech.glide:glide:4.8.0' +} diff --git a/app/hongbao.jks b/app/hongbao.jks new file mode 100644 index 0000000..8f5442b Binary files /dev/null and b/app/hongbao.jks differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..72cd53e --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/group/tonight/hongbao/App.java b/app/src/main/java/group/tonight/hongbao/App.java new file mode 100644 index 0000000..4d7f799 --- /dev/null +++ b/app/src/main/java/group/tonight/hongbao/App.java @@ -0,0 +1,13 @@ +package group.tonight.hongbao; + +import android.app.Application; + +import com.lzy.okgo.OkGo; + +public class App extends Application { + @Override + public void onCreate() { + super.onCreate(); + OkGo.getInstance().init(this); + } +} diff --git a/app/src/main/java/group/tonight/hongbao/HongBaoAccessibilityService.java b/app/src/main/java/group/tonight/hongbao/HongBaoAccessibilityService.java new file mode 100644 index 0000000..89f31af --- /dev/null +++ b/app/src/main/java/group/tonight/hongbao/HongBaoAccessibilityService.java @@ -0,0 +1,120 @@ +package group.tonight.hongbao; + +import android.accessibilityservice.AccessibilityService; +import android.os.Handler; +import android.os.Looper; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.socks.library.KLog; + +import java.util.List; + +public class HongBaoAccessibilityService extends AccessibilityService { + private Handler mHandler = new Handler(Looper.getMainLooper()); + private static final String TAG = HongBaoAccessibilityService.class.getSimpleName(); + //聊天列表 + private static final String LAYOUT_CHAT_LIST = "com.tencent.mm.ui.LauncherUI"; + //拆红包界面 + private static final String LAYOUT_OPEN_RED_BAG = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyNotHookReceiveUI"; + //红包详情 + private static final String LAYOUT_RED_BAG_DETAIL = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI"; + + private static final String LAYOUT_UNKNOW = "com.tencent.mm.ui.base.p"; + + private String mCurrentActivityName; + private boolean mOpenningRedBag; + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + int eventType = event.getEventType(); + CharSequence className = event.getClassName(); + int itemCount = event.getItemCount(); + if (className != null) { + if (className.toString().contains("com.tencent.mm")) { + KLog.e("onAccessibilityEvent: " + eventType + " " + className); + mCurrentActivityName = className.toString(); + } + } + + boolean hasRedBag = hasRedBag(); + KLog.e("onAccessibilityEvent: 有红包--->" + hasRedBag); + switch (eventType) { + case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: + KLog.e("onAccessibilityEvent: TYPE_WINDOW_STATE_CHANGED"); + + break; + case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: +// KLog.e( "onAccessibilityEvent: TYPE_WINDOW_CONTENT_CHANGED"); + KLog.e("onAccessibilityEvent: 有红包--->" + hasRedBag); + + if (mCurrentActivityName != null) { + final AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow(); + switch (mCurrentActivityName) { + case LAYOUT_CHAT_LIST: + if (mOpenningRedBag) { + return; + } + if (hasRedBag()) { + List nodeInfosByViewId = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ao4");//点开红包弹窗 + if (!nodeInfosByViewId.isEmpty()) { + KLog.e("点击最后一个未拆开过的红包"); + nodeInfosByViewId.get(nodeInfosByViewId.size() - 1).performAction(AccessibilityNodeInfo.ACTION_CLICK); + mOpenningRedBag = true; + } + } + break; + case LAYOUT_OPEN_RED_BAG: + for (AccessibilityNodeInfo nodeInfo : rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/cv0")) {//拆开红包 + if (nodeInfo.isClickable() && nodeInfo.getClassName().equals("android.widget.Button")) { + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); + KLog.e("onAccessibilityEvent: 点击拆开红包按钮"); + break; + } + } + break; + case LAYOUT_RED_BAG_DETAIL: + mOpenningRedBag = false; + for (AccessibilityNodeInfo nodeInfo : rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/k4")) {//返回 + if (nodeInfo.isClickable()) { + nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); + break; + } + } + break; + default: + break; + } + } + break; + case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: +// KLog.e( "onAccessibilityEvent: TYPE_NOTIFICATION_STATE_CHANGED"); + break; + default: + break; + } + + } + + private boolean hasRedBag() { + AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow(); + if (rootInActiveWindow == null) { + return false; + } + //每个聊天联系人item + //红包布局,包含红包图标、恭喜发财、已被领完 + List nodeInfoList = rootInActiveWindow.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ao4"); + if (!nodeInfoList.isEmpty()) { + AccessibilityNodeInfo nodeInfo = nodeInfoList.get(nodeInfoList.size() - 1);//最后一个item + + List infoList = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/ape");//已被领完 + return infoList.isEmpty(); + } + return false; + } + + @Override + public void onInterrupt() { + + } +} diff --git a/app/src/main/java/group/tonight/hongbao/MainActivity.java b/app/src/main/java/group/tonight/hongbao/MainActivity.java new file mode 100644 index 0000000..468629d --- /dev/null +++ b/app/src/main/java/group/tonight/hongbao/MainActivity.java @@ -0,0 +1,235 @@ +package group.tonight.hongbao; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.support.v4.content.FileProvider; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.FileCallback; +import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.model.Response; +import com.lzy.okgo.request.base.Request; +import com.socks.library.KLog; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.util.Date; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + findViewById(R.id.setting).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); + } + }); + ImageView imageView = (ImageView) findViewById(R.id.qr_code); + Glide.with(imageView) + .load("http://qr.liantu.com/api.php?text=" + "https://fir.im/zr2t") + .into(imageView); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main_activity, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.actioin_update) { + OkGo.get("https://download.fir.im/zr2t") + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + String json = response.body(); + try { + JSONObject jsonObject = new JSONObject(json); + JSONObject app = jsonObject.getJSONObject("app"); + String id = app.getString("id"); + String download_token = app.getString("token"); + + JSONObject master = app.getJSONObject("releases").getJSONObject("master"); + final String release_id = master.getString("id"); + final String version = master.getString("version"); + final int build = master.getInt("build"); + final String changelog = master.getString("changelog"); + final long fsize = master.getLong("fsize"); + final long created_at = master.getLong("created_at"); + + OkGo.post("https://download.fir.im/apps/" + id + "/install") + .params("download_token", download_token) + .params("release_id", release_id) + .execute(new StringCallback() { + @Override + public void onSuccess(Response response) { + String json = response.body(); + try { + JSONObject jsonObject1 = new JSONObject(json); + final String apkUrl = jsonObject1.getString("url"); + KLog.e("apk下载地址:" + apkUrl); + + final UpdateBean updateBean = new UpdateBean(); + updateBean.setId(release_id); + updateBean.setVersion(version); + updateBean.setBuild(build); + updateBean.setChangelog(changelog); + updateBean.setFsize(fsize); + updateBean.setCreated_at(created_at); + updateBean.setApkUrl(apkUrl); + + KLog.e(updateBean.toString()); + + + PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + String versionName = packageInfo.versionName; + int versionCode = packageInfo.versionCode; + if (versionName.equals(version) && versionCode == build) { + Toast.makeText(MainActivity.this, "已是最新版本", Toast.LENGTH_SHORT).show(); + return; + } + + DecimalFormat format = new DecimalFormat("0.00"); + double fileSize = fsize / 1024.0f / 1024.f; + + StringBuilder builder = new StringBuilder(); + builder.append("版本号:") + .append(build) + .append("\n") + .append("版本名称:") + .append(version) + .append("\n") + .append("Apk大小:") + .append(format.format(fileSize) + "MB") + .append("\n") + .append("发布时间:") + .append("\n") + .append(DateFormat.getDateTimeInstance().format(new Date(created_at * 1000))) + .append("\n") + .append("\n") + .append("更新内容:") + .append("\n") + .append(changelog); + + new AlertDialog.Builder(MainActivity.this) + .setTitle("发现新版本") + .setMessage(builder.toString()) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + } + }) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + + OkGo.get(apkUrl) + .execute(new FileCallback() { + @Override + public void onStart(Request request) { + super.onStart(request); + progressDialog.show(); + } + + @Override + public void onFinish() { + super.onFinish(); + progressDialog.dismiss(); + } + + @Override + public void downloadProgress(Progress progress) { + super.downloadProgress(progress); + float fraction = progress.fraction; + + } + + @Override + public void onSuccess(Response response) { + File body = response.body(); + KLog.e(body.getPath()); + installApk(MainActivity.this, body.getPath()); + } + }); + } + }) + .show(); + } catch (JSONException e) { + e.printStackTrace(); + Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + } + }); + + } catch (JSONException e) { + e.printStackTrace(); + Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + }); + + } + return super.onOptionsItemSelected(item); + } + + /** + * 安装apk + * + * @param context + * @param apkPath + */ + public static void installApk(Context context, String apkPath) { + try { + /** + * provider + * 处理android 7.0 及以上系统安装异常问题 + */ + File file = new File(apkPath); + Intent install = new Intent(Intent.ACTION_VIEW); + install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Uri apkUri = FileProvider.getUriForFile(context, context.getString(R.string.file_provider_authorities), file);//在AndroidManifest中的android:authorities值 + Log.d("======", "apkUri=" + apkUri); + install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加这一句表示对目标应用临时授权该Uri所代表的文件 + install.setDataAndType(apkUri, "application/vnd.android.package-archive"); + } else { + install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); + } + context.startActivity(install); + } catch (Exception e) { + e.printStackTrace(); + Log.d("======", e.getMessage()); + } + } +} diff --git a/app/src/main/java/group/tonight/hongbao/UpdateBean.java b/app/src/main/java/group/tonight/hongbao/UpdateBean.java new file mode 100644 index 0000000..8f2008d --- /dev/null +++ b/app/src/main/java/group/tonight/hongbao/UpdateBean.java @@ -0,0 +1,93 @@ +package group.tonight.hongbao; + +import java.io.Serializable; + +public class UpdateBean implements Serializable { + + /** + * id : 5c2f2886548b7a7d78997185 + * version : 1.0.0 alpha-37 + * build : 11 + * changelog : 修复版本更新内容显示不全的bug + * fsize : 7581932 + * created_at : 1546594438 + */ + + private String id; + private String version; + private long build; + private String changelog; + private long fsize; + private long created_at; + + private String apkUrl; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public long getBuild() { + return build; + } + + public void setBuild(long build) { + this.build = build; + } + + public String getChangelog() { + return changelog; + } + + public void setChangelog(String changelog) { + this.changelog = changelog; + } + + public long getFsize() { + return fsize; + } + + public void setFsize(long fsize) { + this.fsize = fsize; + } + + public long getCreated_at() { + return created_at; + } + + public void setCreated_at(long created_at) { + this.created_at = created_at; + } + + public String getApkUrl() { + return apkUrl; + } + + public void setApkUrl(String apkUrl) { + this.apkUrl = apkUrl; + } + + @Override + public String toString() { + return "UpdateBean{" + + "id='" + id + '\'' + + ", version='" + version + '\'' + + ", build=" + build + + ", changelog='" + changelog + '\'' + + ", fsize=" + fsize + + ", created_at=" + created_at + + ", apkUrl='" + apkUrl + '\'' + + '}'; + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..eaa3dac --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + +