拷贝APP_Bck20250119项目源码,移除libjc/jcc/libs/android-29.jar文件。
This commit is contained in:
1
watchoutputinstaller/.gitignore
vendored
Normal file
1
watchoutputinstaller/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
1
watchoutputinstaller/app_update_description.txt
Normal file
1
watchoutputinstaller/app_update_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
70
watchoutputinstaller/build.gradle
Normal file
70
watchoutputinstaller/build.gradle
Normal 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.watchoutputinstaller"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
// versionName 更新后需要手动设置
|
||||
// .winboll/winbollBuildProps.properties 文件的 stageCount=0
|
||||
// Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
|
||||
versionName "4.1"
|
||||
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'])
|
||||
}
|
8
watchoutputinstaller/build.properties
Normal file
8
watchoutputinstaller/build.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Dec 11 07:50:13 GMT 2024
|
||||
stageCount=3
|
||||
libraryProject=
|
||||
baseVersion=4.1
|
||||
publishVersion=4.1.2
|
||||
buildCount=0
|
||||
baseBetaVersion=4.1.3
|
17
watchoutputinstaller/proguard-rules.pro
vendored
Normal file
17
watchoutputinstaller/proguard-rules.pro
vendored
Normal 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 *;
|
||||
#}
|
22
watchoutputinstaller/src/beta/AndroidManifest.xml
Normal file
22
watchoutputinstaller/src/beta/AndroidManifest.xml
Normal 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.watchoutputinstaller.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>
|
||||
|
6
watchoutputinstaller/src/beta/res/values/strings.xml
Normal file
6
watchoutputinstaller/src/beta/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Watch Output Installer☆</string>
|
||||
|
||||
</resources>
|
73
watchoutputinstaller/src/main/AndroidManifest.xml
Normal file
73
watchoutputinstaller/src/main/AndroidManifest.xml
Normal 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.watchoutputinstaller">
|
||||
|
||||
<!-- 读取您共享存储空间中的内容 -->
|
||||
<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>
|
@@ -0,0 +1,31 @@
|
||||
package cc.winboll.studio.watchoutputinstaller;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,80 @@
|
||||
package cc.winboll.studio.watchoutputinstaller;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,312 @@
|
||||
package cc.winboll.studio.watchoutputinstaller;
|
||||
|
||||
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.watchoutputinstaller.MainActivity;
|
||||
import cc.winboll.studio.watchoutputinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.watchoutputinstaller.services.MainService;
|
||||
import cc.winboll.studio.watchoutputinstaller.utils.NotificationUtil;
|
||||
import cc.winboll.studio.watchoutputinstaller.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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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.watchoutputinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.watchoutputinstaller.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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import cc.winboll.studio.watchoutputinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.watchoutputinstaller.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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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.watchoutputinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.watchoutputinstaller.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,248 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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.watchoutputinstaller.FileListener;
|
||||
import cc.winboll.studio.watchoutputinstaller.MainActivity;
|
||||
import cc.winboll.studio.watchoutputinstaller.beans.AppConfigs;
|
||||
import cc.winboll.studio.watchoutputinstaller.services.AssistantService;
|
||||
import cc.winboll.studio.watchoutputinstaller.services.MainService;
|
||||
import cc.winboll.studio.watchoutputinstaller.utils.NotificationUtil;
|
||||
import cc.winboll.studio.watchoutputinstaller.utils.PackageUtil;
|
||||
import cc.winboll.studio.watchoutputinstaller.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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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.watchoutputinstaller.services.MainService;
|
||||
import cc.winboll.studio.watchoutputinstaller.MainActivity;
|
||||
import cc.winboll.studio.watchoutputinstaller.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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,30 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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 "";
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
package cc.winboll.studio.watchoutputinstaller.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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
11
watchoutputinstaller/src/main/res/drawable/ic_launcher.xml
Normal file
11
watchoutputinstaller/src/main/res/drawable/ic_launcher.xml
Normal 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>
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
11
watchoutputinstaller/src/main/res/drawable/ic_winboll.xml
Normal file
11
watchoutputinstaller/src/main/res/drawable/ic_winboll.xml
Normal 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>
|
@@ -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>
|
@@ -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>
|
||||
|
142
watchoutputinstaller/src/main/res/layout/activity_main.xml
Normal file
142
watchoutputinstaller/src/main/res/layout/activity_main.xml
Normal 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.watchoutputinstaller.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>
|
||||
|
15
watchoutputinstaller/src/main/res/layout/installitem.xml
Normal file
15
watchoutputinstaller/src/main/res/layout/installitem.xml
Normal 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>
|
||||
|
@@ -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>
|
||||
|
9
watchoutputinstaller/src/main/res/values/attrs.xml
Normal file
9
watchoutputinstaller/src/main/res/values/attrs.xml
Normal 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>
|
11
watchoutputinstaller/src/main/res/values/colors.xml
Normal file
11
watchoutputinstaller/src/main/res/values/colors.xml
Normal 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>
|
3
watchoutputinstaller/src/main/res/values/strings.xml
Normal file
3
watchoutputinstaller/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Watch Output Installer</string>
|
||||
</resources>
|
14
watchoutputinstaller/src/main/res/values/themes.xml
Normal file
14
watchoutputinstaller/src/main/res/values/themes.xml
Normal 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>
|
26
watchoutputinstaller/src/main/res/xml/provider_paths.xml
Normal file
26
watchoutputinstaller/src/main/res/xml/provider_paths.xml
Normal 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>
|
||||
|
22
watchoutputinstaller/src/stage/AndroidManifest.xml
Normal file
22
watchoutputinstaller/src/stage/AndroidManifest.xml
Normal 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.watchoutputinstaller.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths"/>
|
||||
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
6
watchoutputinstaller/src/stage/res/values/strings.xml
Normal file
6
watchoutputinstaller/src/stage/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- Put flavor specific strings here -->
|
||||
|
||||
</resources>
|
Reference in New Issue
Block a user