拷贝APP_Bck20250119项目源码,移除libjc/jcc/libs/android-29.jar文件。

This commit is contained in:
ZhanGSKen
2025-01-19 19:59:04 +08:00
commit 65509eacba
654 changed files with 35062 additions and 0 deletions

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,70 @@
apply plugin: 'com.android.application'
apply from: '../.winboll/winboll_app_build.gradle'
apply from: '../.winboll/winboll_lint_build.gradle'
def genVersionName(def versionName){
// 检查编译标志位配置
assert (winbollBuildProps['stageCount'] != null)
assert (winbollBuildProps['baseVersion'] != null)
// 保存基础版本号
winbollBuildProps.setProperty("baseVersion", "${versionName}");
//保存编译标志配置
FileOutputStream fos = new FileOutputStream(winbollBuildPropsFile)
winbollBuildProps.store(fos, "${winbollBuildPropsDesc}");
fos.close();
// 返回编译版本号
return "${versionName}." + winbollBuildProps['stageCount']
}
android {
compileSdkVersion 32
buildToolsVersion "33.0.3"
defaultConfig {
applicationId "cc.winboll.studio.autoinstaller"
minSdkVersion 24
targetSdkVersion 30
versionCode 2
// versionName 更新后需要手动设置
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
versionName "5.0"
if(true) {
versionName = genVersionName("${versionName}")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
api 'cc.winboll.studio:winboll-shared:1.1.0'
api 'io.github.medyo:android-about-page:2.0.0'
api 'com.github.getActivity:ToastUtils:10.5'
api 'com.jcraft:jsch:0.1.55'
api 'org.jsoup:jsoup:1.13.1'
api 'com.squareup.okhttp3:okhttp:4.4.1'
api 'androidx.appcompat:appcompat:1.0.0'
api 'androidx.fragment:fragment:1.0.0'
api 'com.google.android.material:material:1.0.0'
api 'androidx.vectordrawable:vectordrawable-animated:1.0.0'
api 'androidx.lifecycle:lifecycle-livedata:1.0.0'
//api 'cc.winboll.studio:libaes:6.3.2'
//api 'cc.winboll.studio:libapputils:8.3.8'
api fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@@ -0,0 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Thu Jan 02 02:39:56 HKT 2025
stageCount=4
libraryProject=
baseVersion=5.0
publishVersion=5.0.3
buildCount=0
baseBetaVersion=5.0.4

17
autoinstaller/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.autoinstaller.beta.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">AutoInstaller☆</string>
</resources>

View File

@@ -0,0 +1,73 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.winboll.studio.autoinstaller">
<!-- 读取您共享存储空间中的内容 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 修改或删除您共享存储空间中的内容 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 请求安装文件包 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<!-- 运行前台服务 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- 此应用可显示在其他应用上方 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- 开机启动 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- MANAGE_EXTERNAL_STORAGE -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@drawable/ic_winboll"
android:label="@string/app_name"
android:roundIcon="@drawable/ic_winboll"
android:persistent="true"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/Theme.Application">
<activity
android:name=".MainActivity"
android:exported="true"
android:resizeableActivity="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name=".services.MainService"/>
<service android:name=".services.AssistantService"/>
<receiver
android:name=".receivers.MainReceiver"
android:enabled="true"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -0,0 +1,31 @@
package cc.winboll.studio.autoinstaller;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/04/28 02:39:58
* @Describe 全局应用类
*/
import android.view.Gravity;
import com.hjq.toast.ToastUtils;
import cc.winboll.studio.shared.app.WinBollApplication;
public class App extends WinBollApplication {
public static final String TAG = "App";
@Override
public void onCreate() {
super.onCreate();
setIsDebug(BuildConfig.DEBUG);
// 初始化 Toast 框架
ToastUtils.init(this);
// 设置 Toast 布局样式
ToastUtils.setView(R.layout.toast_custom_view);
//ToastUtils.setStyle(new WhiteToastStyle());
ToastUtils.setGravity(Gravity.BOTTOM, 0, 200);
}
}

View File

@@ -0,0 +1,80 @@
package cc.winboll.studio.autoinstaller;
import android.os.FileObserver;
import cc.winboll.studio.shared.log.LogUtils;
public class FileListener extends FileObserver {
public final static String TAG = "FileListener";
public EventCallback callback;
public FileListener(String path) {
super(path);
LogUtils.i(TAG, "File Listener to : " + path);
}
public void setEventCallback(EventCallback callback) {
this.callback = callback;
}
@Override
public void onEvent(int event, String path) {
//LogUtils.d(TAG, "path : "+path);
//int e = event & FileObserver.ALL_EVENTS;
int e = event & FileObserver.CLOSE_WRITE;
//LogUtils.d(TAG, "event->e:"+e);
switch (e) {
case FileObserver.ACCESS:
//LogUtils.d(TAG, "文件操作___" + e + "__1打开文件后读取文件的操作");
break;
case FileObserver.MODIFY:
//LogUtils.d(TAG, "文件操作___" + e + "__2文件被修改");
break;
case FileObserver.ATTRIB:
//LogUtils.d(TAG, "文件操作___" + e + "__4属性变化");
break;
case FileObserver.CLOSE_WRITE:
//LogUtils.d(TAG, "文件操作___" + e + "__8文件写入或编辑后关闭");
callback.onEvent(path);
break;
case FileObserver.CLOSE_NOWRITE:
//录音时,最后一个有效回调是这个
//LogUtils.d(TAG, "文件操作___" + e + "__16只读文件被关闭");
//callback.onEvent(path);
break;
case FileObserver.OPEN:
//LogUtils.d(TAG, "文件操作___" + e + "__32文件被打开");
break;
case FileObserver.MOVED_FROM:
//LogUtils.d(TAG, "文件操作___" + e + "__64移出事件");//试了重命名先MOVED_FROM再MOVED_TO
break;
case FileObserver.MOVED_TO:
//LogUtils.d(TAG, "文件操作___" + e + "__128移入事件");
break;
case FileObserver.CREATE:
//LogUtils.d(TAG, "文件操作___" + e + "__256新建文件");//把文件移动给自己先CREATE在DELETE
break;
case FileObserver.DELETE:
//LogUtils.d(TAG, "文件操作___" + e + "__512有删除文件");//把文件移出去DELETE
break;
case FileObserver.DELETE_SELF:
//LogUtils.d(TAG, "文件操作___" + e + "__1024监听的这个文件夹被删除");
break;
case FileObserver.MOVE_SELF:
//LogUtils.d(TAG, "文件操作___" + e + "__2048监听的这个文件夹被移走");
break;
case FileObserver.ALL_EVENTS:
//LogUtils.d(TAG, "文件操作___" + e + "__4095全部操作");
break;
}
}
public interface EventCallback {
void onEvent(String path);
}
}

View File

@@ -0,0 +1,312 @@
package cc.winboll.studio.autoinstaller;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.SimpleAdapter;
import android.widget.Switch;
import android.widget.TextClock;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import cc.winboll.studio.shared.log.LogUtils;
import cc.winboll.studio.shared.log.LogView;
import cc.winboll.studio.autoinstaller.MainActivity;
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
import cc.winboll.studio.autoinstaller.views.ListViewForScrollView;
import com.hjq.toast.ToastUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
private static final int INSTALL_PERMISSION_CODE = 1;
LogView mLogView;
TextClock mTextClock;
EditText mEditText;
Switch mSwitch;
//sharedPreferences shPrefs;
SimpleAdapter mSimpleAdapter;
static String MAP_NAME = "NAME";
List<Map<String,Object>> mAdapterData = new ArrayList<>();
ListViewForScrollView mListViewForScrollView;
String szAPKFileName;
String szAPKFilePath;
public static final String ACTION_NEW_INSTALLTASK = MainActivity.class.getName() + ".ACTION_NEW_INSTALLTASK";
public static final String EXTRA_INSTALLED_PACKAGENAME = "EXTRA_INSTALLED_PACKAGENAME";
public static final String EXTRA_INSTALLED_APKFILEPATH = "EXTRA_INSTALLED_APKFILEPATH";
public static int REQUEST_CODE_ON_INSTALL_COMPLETED = 0;
String mszInstalledPackageName = "";
String mszInstalledAPKFilePath = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
// Remove this line if you don't want AndroidIDE to show this app's logs
//LogSender.startLogging(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
if (getIntent().getAction().equals(ACTION_NEW_INSTALLTASK)) {
mszInstalledPackageName = getIntent().getStringExtra(EXTRA_INSTALLED_PACKAGENAME);
mszInstalledAPKFilePath = getIntent().getStringExtra(EXTRA_INSTALLED_APKFILEPATH);
installAPK();
}
//LogUtils.d(TAG, "Hello, World!");
}
private void initView() {
// 设置调试日志显示
mLogView = findViewById(R.id.logview);
mLogView.start();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
if (appConfigs == null) {
appConfigs = new AppConfigs();
AppConfigs.saveAppConfigs(this, appConfigs);
}
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(true);
} else if (appConfigs.getSetupMode() == AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO) {
((RadioButton)findViewById(R.id.activitymainRadioButton2)).setChecked(true);
}
NotificationUtil nu = new NotificationUtil();
nu.createServiceNotificationChannel(MainActivity.this);
//Define shared preferences class
//shPrefs = new sharedPreferences();
mTextClock = findViewById(R.id.activitymainTextClock1);
mTextClock.setTimeZone("Asia/Shanghai");
mTextClock.setFormat12Hour("yyyy/MM/dd HH:mm:ss");
mEditText = findViewById(R.id.activitymainEditText1);
//Add text from EditText to Shared Preferences
//mEditText.setText(shPrefs.getFilePath(MainActivity.this));
mEditText.setText(appConfigs.getWatchingFilePath());
mListViewForScrollView = findViewById(R.id.activitymainListViewForScrollView1);
/*Map<String,Object> map1 =new HashMap<>();
map1.put(MAP_NAME, "test1");
mAdapterData.add(map1);
Map<String,Object> map2 =new HashMap<>();
map2.put(MAP_NAME, "test2");
mAdapterData.add(map2);*/
// 绑定适配器与数据
mSimpleAdapter = new SimpleAdapter(MainActivity.this, mAdapterData, R.layout.installitem
, new String[]{MAP_NAME}
, new int[]{R.id.installitemTextView1});
mSimpleAdapter.setDropDownViewResource(R.layout.installitem);
mListViewForScrollView.setAdapter(mSimpleAdapter);
mSwitch = findViewById(R.id.activitymainSwitch1);
//if (shPrefs.getIsLockFilePath(MainActivity.this)) {
if (appConfigs.isEnableService()) {
mSwitch.setChecked(true);
mEditText.setEnabled(false);
mEditText.setBackgroundColor(getColor(R.color.colorLock));
szAPKFilePath = mEditText.getText().toString();
startWatchingFile();
}
}
public void onOpenAPP(View view) {
if (mszInstalledPackageName.trim().equals("")) {
ToastUtils.show("Installed APP package name is null.");
return;
}
Intent intent = getPackageManager().getLaunchIntentForPackage(mszInstalledPackageName);
if (intent != null) {
startActivity(intent);
} else {
// 若没能获取到启动意图,可进行相应提示等操作,比如跳转到应用商店让用户下载该应用(示例)
Intent marketIntent = new Intent(Intent.ACTION_VIEW);
marketIntent.setData(Uri.parse("market://details?id=" + mszInstalledPackageName));
startActivity(marketIntent);
}
}
public void onInstallAPK(View view) {
installAPK();
}
void installAPK() {
if (mszInstalledAPKFilePath.trim().equals("")) {
ToastUtils.show("Installed APK file path is null.");
return;
}
File file = new File(mszInstalledAPKFilePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file);
LogUtils.d(TAG, "Uri is : " + apkUri.toString());
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivityForResult(intent, REQUEST_CODE_ON_INSTALL_COMPLETED);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_ON_INSTALL_COMPLETED) {
// 根据resultCode等判断具体情况这里简单示例显示一个普通吐司
//Toast.makeText(this, "[ " + mszInstalledPackageName + " ]安装窗口已关闭啦", Toast.LENGTH_SHORT).show();
//finish();
}
}
public void onLockPath(View view) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
Switch sw = (Switch)view;
if (sw.isChecked()) {
String szFilePath = mEditText.getText().toString();
// 设置空路径时退出
//
if (szFilePath.trim().equals("")) {
sw.setChecked(false);
Toast.makeText(getApplication(), "监控路径为空。", Toast.LENGTH_SHORT).show();
return;
}
// 设置监控功能
//
File f = new File(szFilePath);
if (f != null && f.getParentFile() != null && f.getParentFile().exists()) {
//shPrefs.saveFilePath(MainActivity.this, mEditText.getText().toString());
appConfigs.setWatchingFilePath(mEditText.getText().toString());
//shPrefs.saveIsLockFilePath(MainActivity.this, true);
appConfigs.setIsEnableService(true);
mEditText.setEnabled(false);
mEditText.setBackgroundColor(getColor(R.color.colorLock));
szAPKFilePath = mEditText.getText().toString();
startWatchingFile();
} /*else {
if (mEditText.getText().toString().trim().equals("")) {
//shPrefs.saveFilePath(MainActivity.this, "");
appConfigs.setWatchingFilePath("");
//shPrefs.saveIsLockFilePath(MainActivity.this, true);
appConfigs.setIsEnableService(true);
mEditText.setEnabled(false);
mEditText.setBackgroundColor(getColor(R.color.colorLock));
szAPKFilePath = "";
} else {
sw.setChecked(false);
}
}*/
} else {
//shPrefs.saveIsLockFilePath(MainActivity.this, false);
appConfigs.setIsEnableService(false);
mEditText.setEnabled(true);
mEditText.setBackgroundColor(Color.TRANSPARENT);
stopWatchingFile();
}
AppConfigs.saveAppConfigs(this, appConfigs);
}
void stopWatchingFile() {
Intent intentService = new Intent(this, MainService.class);
stopService(intentService);
}
void startWatchingFile() {
LogUtils.d(TAG, "startWatchingFile()");
Intent intentService = new Intent(MainActivity.this, MainService.class);
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, szAPKFilePath);
startService(intentService);
}
/*
*Using class allows you to read data from Shared Preferences from other Activities
*/
/*static class sharedPreferences {
//
//Save file path
//
public void saveFilePath(Activity activity, String _filePath) {
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("_filePath", _filePath);
editor.apply();
}
//
//Read file path.
//
public String getFilePath(Activity activity) {
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
return sharedPref.getString("_filePath", "");
}
//
//Set file path lock status.
//
public void saveIsLockFilePath(Activity activity, boolean isLockFilePath) {
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean("isLockFilePath", isLockFilePath);
editor.apply();
}
//
//Read file path lock status.
//
public boolean getIsLockFilePath(Activity activity) {
SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE);
return sharedPref.getBoolean("isLockFilePath", false);
}
}*/
public void onChangeSetupMode(View view) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(this);
if (view.getId() == R.id.activitymainRadioButton1) {
appConfigs.setSetupMode(AppConfigs.SetupMode.WATCHOUTPUTINSTALLER);
((RadioButton)findViewById(R.id.activitymainRadioButton2)).setChecked(false);
} else if (view.getId() == R.id.activitymainRadioButton2) {
appConfigs.setSetupMode(AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO);
((RadioButton)findViewById(R.id.activitymainRadioButton1)).setChecked(false);
}
AppConfigs.saveAppConfigs(this, appConfigs);
}
}

View File

@@ -0,0 +1,145 @@
package cc.winboll.studio.autoinstaller.beans;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/05/27 17:36:01
* @Describe 应用配置数据类
*/
import android.content.Context;
import android.util.JsonReader;
import android.util.JsonWriter;
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
import cc.winboll.studio.autoinstaller.utils.FileUtil;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import cc.winboll.studio.shared.log.LogUtils;
public class AppConfigs implements Serializable {
public static final String TAG = "AppConfigs";
// 安装模式
public static enum SetupMode {
WATCHOUTPUTINSTALLER, // 本应用直接调用安装
NEWAPKINFONEWAPKINFO // 调用[应用信息查看器]打开应用包
};
// 监控文件路径
private String watchingFilePath = "";
// 是否启动服务
private boolean isEnableService = false;
// 安装工具模式
private SetupMode setupMode = SetupMode.WATCHOUTPUTINSTALLER;
public void setWatchingFilePath(String watchingFilePath) {
this.watchingFilePath = watchingFilePath;
}
public String getWatchingFilePath() {
return watchingFilePath;
}
public void setIsEnableService(boolean isEnableService) {
this.isEnableService = isEnableService;
}
public boolean isEnableService() {
return isEnableService;
}
public void setSetupMode(SetupMode setupMode) {
this.setupMode = setupMode;
}
public SetupMode getSetupMode() {
return setupMode;
}
@Override
public String toString() {
// 创建 JsonWriter 对象
StringWriter stringWriter = new StringWriter();
JsonWriter writer = new
JsonWriter(stringWriter);
try {
// 开始 JSON 对象
writer.beginObject();
// 写入键值对
writer.name("watchingFilePath").value(this.watchingFilePath);
writer.name("isEnableService").value(this.isEnableService);
writer.name("setupMode").value(this.setupMode.ordinal());
// 结束 JSON 对象
writer.endObject();
return stringWriter.toString();
} catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
// 获取 JSON 字符串
return "";
}
public static AppConfigs parseAppConfigs(String szAppConfigs) {
AppConfigs appConfigs = new AppConfigs();
// 创建 JsonWriter 对象
StringReader stringReader = new StringReader(szAppConfigs);
JsonReader jsonReader = new
JsonReader(stringReader);
try {
// 开始 JSON 对象
jsonReader.beginObject();
// 写入键值对
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals("watchingFilePath")) {
appConfigs.setWatchingFilePath(jsonReader.nextString());
} else if (name.equals("isEnableService")) {
appConfigs.setIsEnableService(jsonReader.nextBoolean());
} else if (name.equals("setupMode")) {
appConfigs.setSetupMode(SetupMode.values()[jsonReader.nextInt()]);
} else {
jsonReader.skipValue();
}
}
// 结束 JSON 对象
jsonReader.endObject();
return appConfigs;
} catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
// 获取 JSON 字符串
return null;
}
static String getDataPath(Context context) {
return context.getExternalFilesDir(TAG) + "/" + TAG + ".json";
}
public static AppConfigs loadAppConfigs(Context context) {
AppConfigs appConfigs = null;
try {
String szJson = FileUtil.readFile(getDataPath(context));
appConfigs = AppConfigs.parseAppConfigs(szJson);
} catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
return appConfigs;
}
public static void saveAppConfigs(Context context, AppConfigs appConfigs) {
try {
//LogUtils.d(TAG, "appConfigs is : " + appConfigs.toString());
String szJson = appConfigs.toString();
FileUtil.writeFile(getDataPath(context), szJson);
} catch (IOException e) {
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
}
}

View File

@@ -0,0 +1,40 @@
package cc.winboll.studio.autoinstaller.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.shared.log.LogUtils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/06/06 13:19:38
* @Describe 应用消息接收类
*/
public class MainReceiver extends BroadcastReceiver {
public static final String TAG = "MainReceiver";
static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
String szAction = intent.getAction();
if (szAction.equals(ACTION_BOOT_COMPLETED)) {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(context);
if (appConfigs.isEnableService()) {
Intent intentService = new Intent(context, MainService.class);
//intentService.putExtra(MainService.EXTRA_APKFILEPATH, appConfigs.getWatchingFilePath());
if (Build.VERSION.SDK_INT >= 26) {
context.startForegroundService(intentService);
} else {
context.startService(intentService);
}
LogUtils.i(TAG, "System Boot And Start MainService Completed!");
}
}
}
}

View File

@@ -0,0 +1,110 @@
package cc.winboll.studio.autoinstaller.services;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/05/27 20:16:00
* @Describe MainService 守护进程服务
*/
public class AssistantService extends Service {
public static final String TAG = "AssistantService";
//MyBinder mMyBinder;
MyServiceConnection mMyServiceConnection;
volatile boolean mIsThreadAlive;
@Override
public IBinder onBind(Intent intent) {
//return mMyBinder;
return null;
}
@Override
public void onCreate() {
//LogUtils.d(TAG, "call onCreate()");
super.onCreate();
//mMyBinder = new MyBinder();
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
// 设置运行参数
mIsThreadAlive = false;
run();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//LogUtils.d(TAG, "call onStartCommand(...)");
run();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
}
/*class MyBinder extends IMyAidlInterface.Stub {
@Override
public String getServiceName() {
return AssistantService.class.getSimpleName();
}
}*/
@Override
public void onDestroy() {
//LogUtils.d(TAG, "call onDestroy()");
mIsThreadAlive = false;
super.onDestroy();
}
// 运行服务内容
//
void run() {
//LogUtils.d(TAG, "call run()");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
if (appConfigs.isEnableService()) {
if (mIsThreadAlive == false) {
// 设置运行状态
mIsThreadAlive = true;
// 唤醒和绑定主进程
wakeupAndBindMain();
}
}
}
// 唤醒和绑定主进程
//
void wakeupAndBindMain() {
if (ServiceUtil.isServiceAlive(getApplicationContext(), MainService.class.getName()) == false) {
//LogUtils.d(TAG, "wakeupAndBindMain() Wakeup... ControlCenterService");
startForegroundService(new Intent(AssistantService.this, MainService.class));
}
//LogUtils.d(TAG, "wakeupAndBindMain() Bind... ControlCenterService");
bindService(new Intent(AssistantService.this, MainService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
// 主进程与守护进程连接时需要用到此类
//
class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//LogUtils.d(TAG, "call onServiceConnected(...)");
}
@Override
public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceDisconnected(...)");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(AssistantService.this);
if (appConfigs.isEnableService()) {
wakeupAndBindMain();
}
}
}
}

View File

@@ -0,0 +1,248 @@
package cc.winboll.studio.autoinstaller.services;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import cc.winboll.studio.shared.log.LogUtils;
import cc.winboll.studio.autoinstaller.FileListener;
import cc.winboll.studio.autoinstaller.MainActivity;
import cc.winboll.studio.autoinstaller.beans.AppConfigs;
import cc.winboll.studio.autoinstaller.services.AssistantService;
import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.autoinstaller.utils.NotificationUtil;
import cc.winboll.studio.autoinstaller.utils.PackageUtil;
import cc.winboll.studio.autoinstaller.utils.ServiceUtil;
import com.hjq.toast.ToastUtils;
import java.io.File;
import java.lang.ref.WeakReference;
public class MainService extends Service {
public static String TAG = "MainService";
private static boolean _mIsServiceAlive;
//String mszAPKFilePath;
//String mszAPKFileName;
FileListener mFileListener;
public static final String EXTRA_APKFILEPATH = "EXTRA_APKFILEPATH";
final static int MSG_INSTALL_APK = 0;
Handler mHandler;
MyServiceConnection mMyServiceConnection;
MainActivity mInstallCompletedFollowUpActivity;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate()");
_mIsServiceAlive = false;
mHandler = new MyHandler(MainService.this);
if (mMyServiceConnection == null) {
mMyServiceConnection = new MyServiceConnection();
}
run();
}
private void run() {
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
if (appConfigs.isEnableService()) {
if (_mIsServiceAlive == false) {
// 设置运行状态
_mIsServiceAlive = true;
// 显示前台通知栏
NotificationUtil nu = new NotificationUtil();
nu.sendForegroundNotification(MainService.this);
// 唤醒守护进程
wakeupAndBindAssistant();
startWatchingFile(appConfigs.getWatchingFilePath());
LogUtils.d(TAG, "running...");
} else {
LogUtils.d(TAG, "_mIsServiceAlive is " + Boolean.toString(_mIsServiceAlive));
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFileListener != null) {
mFileListener.stopWatching();
mFileListener = null;
LogUtils.d(TAG, "stopWatching");
}
_mIsServiceAlive = false;
LogUtils.d(TAG, "onDestroy()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand");
run();
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
return appConfigs.isEnableService() ? Service.START_STICKY: super.onStartCommand(intent, flags, startId);
}
// 主进程与守护进程连接时需要用到此类
//
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//LogUtils.d(TAG, "call onServiceConnected(...)");
}
@Override
public void onServiceDisconnected(ComponentName name) {
//LogUtils.d(TAG, "call onServiceConnected(...)");
AppConfigs appConfigs = AppConfigs.loadAppConfigs(MainService.this);
if (appConfigs.isEnableService()) {
// 唤醒守护进程
wakeupAndBindAssistant();
}
}
}
// 唤醒和绑定守护进程
//
void wakeupAndBindAssistant() {
if (ServiceUtil.isServiceAlive(getApplicationContext(), AssistantService.class.getName()) == false) {
startService(new Intent(MainService.this, AssistantService.class));
//LogUtils.d(TAG, "call wakeupAndBindAssistant() : Binding... AssistantService");
bindService(new Intent(MainService.this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
}
}
void startWatchingFile(String szAPKFilePath) {
try {
File fTemp = new File(szAPKFilePath);
final File fParentDir = fTemp.getParentFile();
final String szAPKFileName = fTemp.getName();
if (fParentDir.exists()) {
mFileListener = new FileListener(fParentDir.toString());
mFileListener.setEventCallback(new FileListener.EventCallback() {
@Override
public void onEvent(String path) {
//LogUtils.d(TAG, "path : " + path);
File fTemo = new File(fParentDir, path);
if (fTemo.getName().equals(szAPKFileName)) {
Message message = mHandler.obtainMessage(MSG_INSTALL_APK);
message.obj = fTemo.toString();
mHandler.sendMessage(message);
}
}
});
mFileListener.startWatching();
ToastUtils.show("Start watching.");
} else {
// 父级文件夹不存在,就提示用户
Toast.makeText(getApplication(), fParentDir.toString() + " no exist.", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Toast.makeText(getApplication(), szAPKFilePath.toString() + " watching failed.", Toast.LENGTH_SHORT).show();
LogUtils.d(TAG, e.getMessage(), Thread.currentThread().getStackTrace());
}
}
//
// 调用[应用信息查看器]打开应用包
//
private void installAPK(String szAPKFilePath) {
long nTimeNow = System.currentTimeMillis();
/*SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault());
Date d = new Date(nTimeNow);
Map<String,Object> map =new HashMap<>();
map.put(MAP_NAME, dateFormat.format(d));
mAdapterData.add(0, map);
mSimpleAdapter.notifyDataSetChanged();
LogUtils.d(TAG, "Start install APK");*/
File file = new File(szAPKFilePath);
LogUtils.i(TAG, "New APK File is : " + szAPKFilePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClassName("com.kk.xx.newapkinfo", "sk.styk.martin.apkanalyzer.ui.activity.appdetail.oninstall.OnInstallAppDetailActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file);
LogUtils.d(TAG, "Uri is : " + apkUri.toString());
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
this.startActivity(intent);
LogUtils.i(TAG, "Start Install Activity.");
}
//
// 直接调用系统安装工具进行安装
//
void installAPK2(String szAPKFilePath) {
LogUtils.d(TAG, "installAPK2()");
Intent intent = new Intent(this, MainActivity.class);
intent.setAction(MainActivity.ACTION_NEW_INSTALLTASK);
intent.putExtra(MainActivity.EXTRA_INSTALLED_PACKAGENAME, PackageUtil.getPackageNameFromApk(this, szAPKFilePath));
intent.putExtra(MainActivity.EXTRA_INSTALLED_APKFILEPATH, szAPKFilePath);
// Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}
//
//
static class MyHandler extends Handler {
WeakReference<MainService> weakReference;
MyHandler(MainService service) {
weakReference = new WeakReference<MainService>(service);
}
public void handleMessage(Message message) {
MainService theActivity = weakReference.get();
switch (message.what) {
case MSG_INSTALL_APK:
{
AppConfigs appConfigs = AppConfigs.loadAppConfigs(theActivity);
if (appConfigs.getSetupMode() == AppConfigs.SetupMode.WATCHOUTPUTINSTALLER) {
theActivity.installAPK2((String)message.obj);
} else if (appConfigs.getSetupMode() == AppConfigs.SetupMode.NEWAPKINFONEWAPKINFO) {
theActivity.installAPK((String)message.obj);
}
break;
}
default:
break;
}
super.handleMessage(message);
}
}
}

View File

@@ -0,0 +1,47 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/05/27 17:56:31
* @Describe 文件管理类
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
public class FileUtil {
public static final String TAG = "FileUtil";
//
// 把字符串写入文件,指定 UTF-8 编码
//
public static void writeFile(String filePath, String content) throws IOException {
File file = new File(filePath);
FileOutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
writer.write(content);
writer.close();
}
//
// 读取文件到字符串,指定 UTF-8 编码
//
public static String readFile(String filePath) throws IOException {
File file = new File(filePath);
FileInputStream inputStream = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
StringBuilder content = new StringBuilder();
int character;
while ((character = reader.read()) != -1) {
content.append((char) character);
}
reader.close();
return content.toString();
}
}

View File

@@ -0,0 +1,82 @@
package cc.winboll.studio.autoinstaller.utils;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.os.Build;
import android.util.Log;
import android.widget.RemoteViews;
import cc.winboll.studio.autoinstaller.services.MainService;
import cc.winboll.studio.autoinstaller.MainActivity;
import cc.winboll.studio.autoinstaller.R;
public class NotificationUtil {
static final String TAG = "NotificationUtil";
static final String szServiceChannelID = "0";
static int mNumSendForegroundNotification = 10000;
public NotificationManager createServiceNotificationChannel(Context context) {
//创建通知渠道ID
String channelId = szServiceChannelID;
//创建通知渠道名称
String channelName = "Service Message";
//创建通知渠道重要性
int importance = NotificationManager.IMPORTANCE_MIN;
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.setSound(null, null);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
return notificationManager;
}
// 创建通知
//
public void sendForegroundNotification(MainService service) {
//创建Notification传入Context和channelId
Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
intent.setClass(service, MainActivity.class);
//这里放一个count用来区分每一个通知
//intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
//参数1:context 上下文对象
//参数2:发送者私有的请求码(Private request code for the sender)
//参数3:intent 意图对象
//参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
PendingIntent mForegroundPendingIntent = PendingIntent.getActivity(service, mNumSendForegroundNotification, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT);
Notification mForegroundNotification = new Notification.Builder(service, szServiceChannelID)
.setAutoCancel(true)
.setContentTitle(service.getString(R.string.app_name))
.setContentText(service.TAG + " is started.")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_launcher)
//设置红色
.setColor(Color.parseColor("#F00606"))
.setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
.setContentIntent(mForegroundPendingIntent)
.build();
/*RemoteViews mrvForegroundNotificationView = new RemoteViews(service.getPackageName(), R.layout.remoteview);
mrvForegroundNotificationView.setTextViewText(R.id.remoteviewTextView1, notificationMessage.getTitle());
mrvForegroundNotificationView.setTextViewText(R.id.remoteviewTextView2, notificationMessage.getContent());
mrvForegroundNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
mForegroundNotification.contentView = mrvForegroundNotificationView;
mForegroundNotification.bigContentView = mrvForegroundNotificationView;
*/
service.startForeground(mNumSendForegroundNotification, mForegroundNotification);
}
}

View File

@@ -0,0 +1,30 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/12/11 06:28:50
* @Describe 一个获取安卓APK安装文件的应用包名的函数
*/
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import cc.winboll.studio.shared.log.LogUtils;
public class PackageUtil {
public static final String TAG = "PackageUtil";
public static String getPackageNameFromApk(Context context, String apkFilePath) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = null;
try {
packageInfo = packageManager.getPackageArchiveInfo(apkFilePath, 0);
if (packageInfo != null) {
return packageInfo.packageName;
}
} catch (Exception e) {
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
}
return "";
}
}

View File

@@ -0,0 +1,35 @@
package cc.winboll.studio.autoinstaller.utils;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2024/05/27 20:20:03
* @Describe 服务类工具
*/
import android.app.ActivityManager;
import android.content.Context;
import java.util.List;
public class ServiceUtil {
public final static String TAG = "ServiceUtil";
public static boolean isServiceAlive(Context context, String szServiceName) {
// 获取Activity管理者对象
ActivityManager manager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
// 获取正在运行的服务此处设置最多取1000个
List<ActivityManager.RunningServiceInfo> runningServices = manager
.getRunningServices(1000);
if (runningServices.size() <= 0) {
return false;
}
// 遍历若存在名字和传入的serviceName的一致则说明存在
for (ActivityManager.RunningServiceInfo runningServiceInfo : runningServices) {
if (runningServiceInfo.service.getClassName().equals(szServiceName)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,77 @@
package cc.winboll.studio.autoinstaller.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class ListViewForScrollView extends ListView {
static int nMaxWidth = 0;
public ListViewForScrollView(Context context) {
super(context);
}
public ListViewForScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewForScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 重写onMeasure方法重新计算高度达到使ListView适应ScrollView的效果
*
* @param widthMeasureSpec 宽度测量规则
* @param heightMeasureSpec 高度测量规则
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*
// 子控件TextView不换行显示
ListAdapter listAdapter = this.getAdapter();
if (listAdapter == null) {
return;
}
int maxWidth = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, this);
View cb = listItem.findViewById(R.id.listviewfiledataCheckBox1);
View iv = listItem.findViewById(R.id.listviewfiledataImageView1);
View tv = listItem.findViewById(R.id.listviewfiledataTextView1);
cb.measure(0,0);
iv.measure(0,0);
tv.measure(0,0);
//listItem.measure(0, 0);
//int width = listItem.getMeasuredWidth();
int width = cb.getMeasuredWidth() + iv.getMeasuredWidth()+ tv.getMeasuredWidth();
if(width>maxWidth) maxWidth = width;
}
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
*/
// 子控件TextView换行显示
//Integer.MAX_VALUE:表示int类型能够表示的最大值值为2的31次方-1
//>>2:右移N位相当于除以2的N的幂
//MeasureSpec.AT_MOST子布局可以根据自己的大小选择任意大小的模式
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
//int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthMeasureSpec, MeasureSpec.AT_MOST);
//int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightMeasureSpec, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item android:drawable="@drawable/ic_launcher_background"/>
<item
android:left="15dp"
android:top="15dp"
android:right="15dp"
android:bottom="15dp"
android:drawable="@drawable/ic_launcher_foreground"/>
</layer-list>

View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#FF009DCB"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
</vector>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
android:clickable="true">
<item android:drawable="@drawable/ic_launcher_background"/>
<item
android:left="15dp"
android:top="15dp"
android:right="15dp"
android:bottom="15dp"
android:drawable="@drawable/ic_launcher_foreground"/>
</layer-list>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="180"
android:endColor="@color/purple_500"
android:startColor="@color/purple_200"
android:type="linear" />
<corners android:radius="10dp" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
</LinearLayout>

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Watching File :"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<EditText
android:layout_width="0dp"
android:ems="10"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:id="@+id/activitymainEditText1"/>
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onLockPath"
android:id="@+id/activitymainSwitch1"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/activitymainRadioButton1"
android:text="直接安装"
android:onClick="onChangeSetupMode"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/activitymainRadioButton2"
android:text="用[应用信息查看器]打开"
android:onClick="onChangeSetupMode"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Times Now :"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<AnalogClock
android:layout_width="40dp"
android:layout_height="40dp"/>
<TextClock
android:textSize="20sp"
android:id="@+id/activitymainTextClock1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"/>
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Installed Times :"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:gravity="center"
android:layout_weight="1.0">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<cc.winboll.studio.autoinstaller.views.ListViewForScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/activitymainListViewForScrollView1"/>
</ScrollView>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="安装应用包"
android:onClick="onInstallAPK"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="打开应用"
android:onClick="onOpenAPP"/>
</LinearLayout>
<cc.winboll.studio.shared.log.LogView
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="@+id/logview"/>
</LinearLayout>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text"
android:id="@+id/installitemTextView1"/>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_gradient"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textStyle="bold"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_winboll"/>
<TextView
android:id="@android:id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:textColor="#FFFFFFFF"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LogView">
<attr name="tagList" format="string" />
<attr name="levelDefault" format="string" />
<attr name="textColor" format="color" />
<attr name="textIsSelectable" format="boolean" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="colorLock">#E8E8E8</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">AutoInstaller</string>
</resources>

View File

@@ -0,0 +1,14 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Application" parent="android:Theme.Material.Light.DarkActionBar">
<!-- Primary brand color. -->
<item name="android:colorPrimary">@color/purple_500</item>
<!-- Secondary brand color. -->
<item name="android:colorSecondary">@color/teal_200</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">@color/purple_700</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="." />
<files-path
name="files-path"
path="." />
<cache-path
name="cache-path"
path="." />
<!--/storage/emulated/0/Android/data/...-->
<external-files-path
name="external_file_path"
path="." />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
<external-cache-path
name="external_cache_path"
path="." />
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录否则微信分身保存的图片就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg在小米6的手机上微信分身有这个crash华为没有
-->
<root-path
name="root-path"
path="" />
</paths>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.autoinstaller.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Put flavor specific strings here -->
</resources>