调试代码,加入AES转移模块。
This commit is contained in:
@@ -45,6 +45,8 @@ android {
|
||||
|
||||
dependencies {
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
api 'com.google.code.gson:gson:2.10.1'
|
||||
|
||||
// SSH
|
||||
api 'com.jcraft:jsch:0.1.55'
|
||||
@@ -67,7 +69,7 @@ dependencies {
|
||||
//api 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
|
||||
//api 'androidx.fragment:fragment:1.1.0'
|
||||
|
||||
api 'cc.winboll.studio:libaes:15.8.0'
|
||||
api 'cc.winboll.studio:libapputils:15.8.2'
|
||||
api 'cc.winboll.studio:libappbase:15.8.2'
|
||||
api 'cc.winboll.studio:libaes:15.10.0'
|
||||
api 'cc.winboll.studio:libapputils:15.10.0'
|
||||
api 'cc.winboll.studio:libappbase:15.10.9'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/28 19:12:12
|
||||
* @Describe 应用主要服务组件类守护进程服务组件类
|
||||
*/
|
||||
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.libaes.models.WinBoLLClientServiceBean;
|
||||
import cc.winboll.studio.libaes.winboll.AssistantService;
|
||||
import cc.winboll.studio.libapputils.utils.ServiceUtils;
|
||||
|
||||
public class AssistantService extends Service {
|
||||
|
||||
public final static String TAG = "AssistantService";
|
||||
|
||||
WinBoLLClientServiceBean mWinBoLLServiceBean;
|
||||
MyServiceConnection mMyServiceConnection;
|
||||
volatile boolean mIsServiceRunning;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
|
||||
if (mMyServiceConnection == null) {
|
||||
mMyServiceConnection = new MyServiceConnection();
|
||||
}
|
||||
// 设置运行参数
|
||||
mIsServiceRunning = false;
|
||||
run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
run();
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mIsServiceRunning = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
//
|
||||
// 运行服务内容
|
||||
//
|
||||
void run() {
|
||||
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(this);
|
||||
if (mWinBoLLServiceBean.isEnable()) {
|
||||
if (mIsServiceRunning == false) {
|
||||
// 设置运行状态
|
||||
mIsServiceRunning = true;
|
||||
// 唤醒和绑定主进程
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 唤醒和绑定主进程
|
||||
//
|
||||
void wakeupAndBindMain() {
|
||||
if (ServiceUtils.isServiceAlive(getApplicationContext(), WinBoLLClientService.class.getName()) == false) {
|
||||
startForegroundService(new Intent(AssistantService.this, WinBoLLClientService.class));
|
||||
}
|
||||
|
||||
bindService(new Intent(AssistantService.this, WinBoLLClientService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
|
||||
}
|
||||
|
||||
//
|
||||
// 主进程与守护进程连接时需要用到此类
|
||||
//
|
||||
class MyServiceConnection implements ServiceConnection {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mWinBoLLServiceBean = WinBoLLClientServiceBean.loadWinBoLLClientServiceBean(AssistantService.this);
|
||||
if (mWinBoLLServiceBean.isEnable()) {
|
||||
wakeupAndBindMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/03/28 19:11:27
|
||||
* @Describe WinBoLL UI 状态图标枚举
|
||||
*/
|
||||
import cc.winboll.studio.libaes.R;
|
||||
|
||||
public enum EWUIStatusIconDrawable {
|
||||
NORMAL(0),
|
||||
NEWS(1)
|
||||
;
|
||||
|
||||
static final String TAG = "WUIStatusIconDrawable";
|
||||
|
||||
static String[] _mlistCNName = { "正常", "新的消息" };
|
||||
|
||||
private int value = 0;
|
||||
private EWUIStatusIconDrawable(int value) { //必须是private的,否则编译错误
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static int getIconDrawableId(EWUIStatusIconDrawable drawableId) {
|
||||
int res;
|
||||
switch(drawableId){
|
||||
case NEWS :
|
||||
res = R.drawable.ic_winbollbeta;
|
||||
break;
|
||||
default :
|
||||
res = R.drawable.ic_winboll;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
public static final String TAG = "MainActivity";
|
||||
|
||||
LogView mLogView;
|
||||
private TerminalView mTerminalView;
|
||||
//private TerminalView mTerminalView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -37,10 +37,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
mLogView = findViewById(R.id.logview);
|
||||
|
||||
mTerminalView = findViewById(R.id.terminalView);
|
||||
/*mTerminalView = findViewById(R.id.terminalView);
|
||||
|
||||
// 示例:模拟用户输入 "ls" 命令
|
||||
mTerminalView.sendInput("ls");
|
||||
mTerminalView.sendInput("ls");*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package cc.winboll.studio.appbase;
|
||||
package cc.winboll.studio.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/13 19:30:10
|
||||
*/
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.service.quicksettings.TileService;
|
||||
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||
import cc.winboll.studio.appbase.services.MainService;
|
||||
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||
import cc.winboll.studio.winboll.services.MainService;
|
||||
|
||||
public class MyTileService extends TileService {
|
||||
public static final String TAG = "MyTileService";
|
||||
|
||||
40
winboll/src/main/java/cc/winboll/studio/winboll/WinBoLL.java
Normal file
40
winboll/src/main/java/cc/winboll/studio/winboll/WinBoLL.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 10:13
|
||||
* @Describe WinBoLL 系列应用通用管理类
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libaes.models.WinBoLLModel;
|
||||
|
||||
public class WinBoLL {
|
||||
|
||||
public static final String TAG = "WinBoLL";
|
||||
|
||||
public static final String ACTION_BIND = WinBoLL.class.getName() + ".ACTION_BIND";
|
||||
public static final String EXTRA_WINBOLLMODEL = "EXTRA_WINBOLLMODEL";
|
||||
|
||||
public static void bindToAPPBase(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBase(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
public static void bindToAPPBaseBeta(Context context, String appMainService) {
|
||||
LogUtils.d(TAG, "bindToAPPBaseBeta(...)");
|
||||
String toPackage = "cc.winboll.studio.appbase.beta";
|
||||
startBind(context, toPackage, appMainService);
|
||||
}
|
||||
|
||||
static void startBind(Context context, String toPackage, String appMainService) {
|
||||
Intent intent = new Intent(ACTION_BIND);
|
||||
intent.putExtra(EXTRA_WINBOLLMODEL, (new WinBoLLModel(toPackage, appMainService)).toString());
|
||||
intent.setPackage(toPackage);
|
||||
LogUtils.d(TAG, String.format("ACTION_BIND :\nTo Package : %s\nAPP Main Service : %s", toPackage, appMainService));
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/03 19:28
|
||||
*/
|
||||
public class WinBoLLClientService extends Service {
|
||||
|
||||
public static final String TAG = "WinBoLLClientService";
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cc.winboll.studio.libaes.winboll;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen
|
||||
* @Date 2025/05/03 19:14
|
||||
*/
|
||||
public class WinBoLLServiceStatusView extends LinearLayout {
|
||||
|
||||
public static final String TAG = "WinBoLLServiceStatusView";
|
||||
|
||||
public WinBoLLServiceStatusView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public WinBoLLServiceStatusView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public WinBoLLServiceStatusView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
|
||||
void setServerHost(String szWinBoLLServerHost) {
|
||||
|
||||
}
|
||||
|
||||
void setAuthInfo(String szDevUserName, String szDevUserPassword) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.activities;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
@@ -7,14 +7,14 @@ import android.widget.RadioButton;
|
||||
import cc.winboll.studio.libappbase.BuildConfig;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
import cc.winboll.studio.libappbase.models.UserInfoModel;
|
||||
import cc.winboll.studio.libappbase.utils.RSAUtils;
|
||||
import cc.winboll.studio.libappbase.utils.YunUtils;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import cc.winboll.studio.winboll.utils.YunUtils;
|
||||
import cc.winboll.studio.winboll.models.UserInfoModel;
|
||||
import cc.winboll.studio.winboll.utils.RSAUtils;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.activities;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -11,9 +11,10 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
|
||||
public class New2Activity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
|
||||
@@ -51,15 +52,15 @@ public class New2Activity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
}
|
||||
|
||||
public void onCloseThisActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().finish(this);
|
||||
WinBoLLActivityManager.getInstance().finish(this);
|
||||
}
|
||||
|
||||
public void onCloseAllActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
||||
WinBoLLActivityManager.getInstance().finishAll();
|
||||
}
|
||||
|
||||
public void onNewActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.activities;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
@@ -11,9 +11,10 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
|
||||
public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
|
||||
@@ -50,15 +51,15 @@ public class NewActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
}
|
||||
|
||||
public void onCloseThisActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().finish(this);
|
||||
WinBoLLActivityManager.getInstance().finish(this);
|
||||
}
|
||||
|
||||
public void onCloseAllActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().finishAll();
|
||||
WinBoLLActivityManager.getInstance().finishAll();
|
||||
}
|
||||
|
||||
public void onNew2Activity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, New2Activity.class);
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, New2Activity.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.activities;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -6,13 +6,15 @@ package cc.winboll.studio.appbase.activities;
|
||||
* @Describe WinBoLL 窗口基础类
|
||||
*/
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import cc.winboll.studio.appbase.MainActivity;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.winboll.MainActivity;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
|
||||
public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
|
||||
|
||||
@@ -37,10 +39,10 @@ public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.item_log) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startLogActivity(this);
|
||||
WinBoLLActivityManager.getInstance().startLogActivity(this);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.item_home) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(getApplicationContext(), MainActivity.class);
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
return true;
|
||||
}
|
||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||
@@ -50,12 +52,12 @@ public class WinBoLLActivity extends Activity implements IWinBoLLActivity {
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
GlobalApplication.getWinBoLLActivityManager().add(this);
|
||||
WinBoLLActivityManager.getInstance().add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
GlobalApplication.getWinBoLLActivityManager().registeRemove(this);
|
||||
WinBoLLActivityManager.getInstance().registeRemove(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
@@ -9,12 +9,19 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.libappbase.CrashHandler;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.GlobalCrashActivity;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||
import cc.winboll.studio.winboll.App;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.winboll.services.MainService;
|
||||
import cc.winboll.studio.winboll.services.TestDemoBindService;
|
||||
import cc.winboll.studio.winboll.services.TestDemoService;
|
||||
import cc.winboll.studio.winboll.sos.SOS;
|
||||
import cc.winboll.studio.winboll.widgets.StatusWidget;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLLActivityManager;
|
||||
|
||||
public class WinBoLLUnitTestActivity extends Activity {
|
||||
|
||||
@@ -44,9 +51,9 @@ public class WinBoLLUnitTestActivity extends Activity {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if(item.getItemId() == R.id.item_yun) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class);
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.YunActivity.class);
|
||||
} else if(item.getItemId() == R.id.item_logon) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class);
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, cc.winboll.studio.libappbase.activities.LogonActivity.class);
|
||||
}
|
||||
// 在switch语句中处理每个ID,并在处理完后返回true,未处理的情况返回false。
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -63,7 +70,7 @@ public class WinBoLLUnitTestActivity extends Activity {
|
||||
public void onSwitchDebugMode(View view) {
|
||||
boolean isDebuging = ((CheckBox)view).isChecked();
|
||||
GlobalApplication.setIsDebuging(isDebuging);
|
||||
GlobalApplication.saveDebugStatus();
|
||||
GlobalApplication.saveDebugStatus(this);
|
||||
}
|
||||
|
||||
public void onPreviewGlobalCrashActivity(View view) {
|
||||
@@ -152,7 +159,7 @@ public class WinBoLLUnitTestActivity extends Activity {
|
||||
}
|
||||
|
||||
public void onTestOpenNewActivity(View view) {
|
||||
GlobalApplication.getWinBoLLActivityManager().startWinBoLLActivity(this, NewActivity.class);
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(this, NewActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.activities;
|
||||
package cc.winboll.studio.winboll.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.handlers;
|
||||
package cc.winboll.studio.winboll.handlers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -6,8 +6,8 @@ package cc.winboll.studio.appbase.handlers;
|
||||
*/
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import cc.winboll.studio.appbase.services.MainService;
|
||||
import java.lang.ref.WeakReference;
|
||||
import cc.winboll.studio.winboll.services.MainService;
|
||||
|
||||
public class MainServiceHandler extends Handler {
|
||||
public static final String TAG = "MainServiceHandler";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.models;
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/05 11:26
|
||||
*/
|
||||
|
||||
public class ResponseData {
|
||||
|
||||
public static final String STATUS_SUCCESS = "success";
|
||||
public static final String STATUS_ERROR = "error";
|
||||
|
||||
private String status;
|
||||
private String message;
|
||||
private UserInfoModel data;
|
||||
|
||||
public ResponseData() {
|
||||
this.status = "";
|
||||
this.message = "";
|
||||
this.data = new UserInfoModel();
|
||||
}
|
||||
|
||||
public ResponseData(String status, String message, UserInfoModel data) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setData(UserInfoModel data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public UserInfoModel getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.models;
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.models;
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/04 19:14
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class UserInfoModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "UserInfoModel";
|
||||
|
||||
String username;
|
||||
String password;
|
||||
String token;
|
||||
|
||||
public UserInfoModel() {
|
||||
this.username = "";
|
||||
this.password = "";
|
||||
this.token = "";
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return UserInfoModel.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("username").value(getUsername());
|
||||
jsonWriter.name("password").value(getPassword());
|
||||
jsonWriter.name("token").value(getToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("username")) {
|
||||
setUsername(jsonReader.nextString());
|
||||
} else if (name.equals("password")) {
|
||||
setPassword(jsonReader.nextString());
|
||||
} else if (name.equals("token")) {
|
||||
setToken(jsonReader.nextString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 10:16
|
||||
* @Describe WinBoLLModel
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
import cc.winboll.studio.libappbase.APPModel;
|
||||
|
||||
public class WinBoLLModel extends BaseBean {
|
||||
|
||||
public static final String TAG = "WinBoLLModel";
|
||||
|
||||
String appPackageName;
|
||||
String appMainServiveName;
|
||||
|
||||
public WinBoLLModel() {
|
||||
this.appPackageName = "";
|
||||
this.appMainServiveName = "";
|
||||
}
|
||||
|
||||
public WinBoLLModel(boolean isDebuging, String appPackageName, String appMainServiveName) {
|
||||
this.appPackageName = appPackageName;
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public WinBoLLModel(String appPackageName, String appMainServiveName) {
|
||||
this.appPackageName = appPackageName;
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public void setAppPackageName(String appPackageName) {
|
||||
this.appPackageName = appPackageName;
|
||||
}
|
||||
|
||||
public String getAppPackageName() {
|
||||
return appPackageName;
|
||||
}
|
||||
|
||||
public void setAppMainServiveName(String appMainServiveName) {
|
||||
this.appMainServiveName = appMainServiveName;
|
||||
}
|
||||
|
||||
public String getAppMainServiveName() {
|
||||
return appMainServiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return APPModel.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
|
||||
super.writeThisToJsonWriter(jsonWriter);
|
||||
jsonWriter.name("appPackageName").value(getAppPackageName());
|
||||
jsonWriter.name("appMainServiveName").value(getAppMainServiveName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
|
||||
if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
|
||||
if (name.equals("appPackageName")) {
|
||||
setAppPackageName(jsonReader.nextString());
|
||||
} else if (name.equals("appMainServiveName")) {
|
||||
setAppMainServiveName(jsonReader.nextString());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBean readBeanFromJsonReader(JsonReader jsonReader) throws IOException {
|
||||
jsonReader.beginObject();
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (!initObjectsFromJsonReader(jsonReader, name)) {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
// 结束 JSON 对象
|
||||
jsonReader.endObject();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package cc.winboll.studio.libappbase.models;
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
package cc.winboll.studio.winboll.models;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/05/10 09:36
|
||||
* @Describe WinBoLL 应用消息数据模型
|
||||
*/
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonWriter;
|
||||
import cc.winboll.studio.libappbase.BaseBean;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WinBoLLNewsBean extends BaseBean {
|
||||
|
||||
public static final String TAG = "WinBoLLNewsBean";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.receivers;
|
||||
package cc.winboll.studio.winboll.receivers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -7,8 +7,8 @@ package cc.winboll.studio.appbase.receivers;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||
|
||||
public class APPNewsWidgetClickListener extends BroadcastReceiver {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.receivers;
|
||||
package cc.winboll.studio.winboll.receivers;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -10,14 +10,17 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import cc.winboll.studio.appbase.services.MainService;
|
||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||
import cc.winboll.studio.libaes.winboll.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLL;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
||||
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
|
||||
import cc.winboll.studio.libappbase.sos.SOS;
|
||||
import cc.winboll.studio.libappbase.sos.SOSObject;
|
||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libapputils.utils.AppUtils;
|
||||
import cc.winboll.studio.winboll.models.WinBoLLModel;
|
||||
import cc.winboll.studio.winboll.models.WinBoLLNewsBean;
|
||||
import cc.winboll.studio.winboll.services.MainService;
|
||||
import cc.winboll.studio.winboll.sos.SOS;
|
||||
import cc.winboll.studio.winboll.sos.SOSObject;
|
||||
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.services;
|
||||
package cc.winboll.studio.winboll.services;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -11,11 +11,9 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||
import cc.winboll.studio.appbase.services.AssistantService;
|
||||
import cc.winboll.studio.appbase.services.MainService;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import android.os.Binder;
|
||||
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||
|
||||
public class AssistantService extends Service {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.services;
|
||||
package cc.winboll.studio.winboll.services;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -17,16 +17,15 @@ import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.appbase.MyTileService;
|
||||
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.appbase.models.MainServiceBean;
|
||||
import cc.winboll.studio.appbase.receivers.MainReceiver;
|
||||
import cc.winboll.studio.appbase.services.AssistantService;
|
||||
import cc.winboll.studio.appbase.threads.MainServiceThread;
|
||||
import cc.winboll.studio.appbase.widgets.APPNewsWidget;
|
||||
import cc.winboll.studio.libaes.models.WinBoLLModel;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.models.APPModel;
|
||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
||||
import cc.winboll.studio.winboll.MyTileService;
|
||||
import cc.winboll.studio.winboll.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.winboll.models.MainServiceBean;
|
||||
import cc.winboll.studio.winboll.receivers.MainReceiver;
|
||||
import cc.winboll.studio.winboll.services.AssistantService;
|
||||
import cc.winboll.studio.winboll.threads.MainServiceThread;
|
||||
import cc.winboll.studio.winboll.widgets.APPNewsWidget;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class MainService extends Service {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.services;
|
||||
package cc.winboll.studio.winboll.services;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -10,12 +10,12 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.appbase.App;
|
||||
import cc.winboll.studio.appbase.models.TestDemoBindServiceBean;
|
||||
import cc.winboll.studio.appbase.services.TestDemoBindService;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLL;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.sos.SOS;
|
||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
|
||||
import cc.winboll.studio.winboll.App;
|
||||
import cc.winboll.studio.winboll.models.TestDemoBindServiceBean;
|
||||
import cc.winboll.studio.winboll.services.TestDemoBindService;
|
||||
import cc.winboll.studio.winboll.sos.SOS;
|
||||
|
||||
public class TestDemoBindService extends Service {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.services;
|
||||
package cc.winboll.studio.winboll.services;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -10,8 +10,9 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.appbase.models.TestDemoServiceBean;
|
||||
import cc.winboll.studio.winboll.models.TestDemoServiceBean;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.winboll.models.TestDemoServiceBean;
|
||||
|
||||
public class TestDemoService extends Service {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
package cc.winboll.studio.winboll.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
package cc.winboll.studio.winboll.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
package cc.winboll.studio.winboll.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
package cc.winboll.studio.winboll.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.sos;
|
||||
package cc.winboll.studio.winboll.sos;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package cc.winboll.studio.appbase.threads;
|
||||
package cc.winboll.studio.winboll.threads;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/14 03:46:44
|
||||
*/
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.appbase.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.winboll.handlers.MainServiceHandler;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
package cc.winboll.studio.winboll.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/04 13:36
|
||||
* @Describe RSA加密工具
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.util.Base64;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Objects;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class RSAUtils {
|
||||
private static final String TAG = "RSAUtils";
|
||||
private static final int KEY_SIZE = 2048;
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
private static final String PUBLIC_KEY_FILE = "public.key";
|
||||
private static final String PRIVATE_KEY_FILE = "private.key";
|
||||
private static final String CIPHER_ALGORITHM = KEY_ALGORITHM + "/ECB/PKCS1Padding"; // 保留原加密方式
|
||||
|
||||
private final String keyPath;
|
||||
private static volatile RSAUtils INSTANCE;
|
||||
|
||||
/**
|
||||
* 构造方法:初始化密钥存储路径(内部存储)
|
||||
*/
|
||||
private RSAUtils(Context context) {
|
||||
keyPath = context.getFilesDir() + File.separator + "keys" + File.separator; // 修正路径格式
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
*/
|
||||
public static synchronized RSAUtils getInstance(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new RSAUtils(context);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密钥文件是否存在
|
||||
*/
|
||||
public boolean keysExist() {
|
||||
File publicKeyFile = new File(keyPath + PUBLIC_KEY_FILE);
|
||||
File privateKeyFile = new File(keyPath + PRIVATE_KEY_FILE);
|
||||
return publicKeyFile.exists() && privateKeyFile.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥对并保存到文件
|
||||
*/
|
||||
public void generateAndSaveKeys() throws Exception {
|
||||
LogUtils.d(TAG, "开始生成 RSA 密钥对(2048位)");
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
||||
generator.initialize(KEY_SIZE);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
|
||||
saveKey(PUBLIC_KEY_FILE, keyPair.getPublic().getEncoded());
|
||||
saveKey(PRIVATE_KEY_FILE, keyPair.getPrivate().getEncoded());
|
||||
LogUtils.d(TAG, "密钥对生成并保存成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或生成密钥对(线程安全)
|
||||
*/
|
||||
public KeyPair getOrGenerateKeys() throws Exception {
|
||||
if (!keysExist()) {
|
||||
synchronized (RSAUtils.class) { // 双重检查锁,避免多线程重复生成
|
||||
if (!keysExist()) {
|
||||
generateAndSaveKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
return readKeysFromFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件读取密钥对
|
||||
*/
|
||||
private KeyPair readKeysFromFile() throws Exception {
|
||||
LogUtils.d(TAG, "读取密钥对文件");
|
||||
try {
|
||||
byte[] publicKeyBytes = readFileToBytes(keyPath + PUBLIC_KEY_FILE);
|
||||
byte[] privateKeyBytes = readFileToBytes(keyPath + PRIVATE_KEY_FILE);
|
||||
|
||||
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(publicKeyBytes);
|
||||
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyBytes);
|
||||
|
||||
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
PublicKey publicKey = factory.generatePublic(publicSpec);
|
||||
PrivateKey privateKey = factory.generatePrivate(privateSpec);
|
||||
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
LogUtils.e(TAG, "密钥文件读取失败:" + e.getMessage());
|
||||
throw new Exception("密钥文件损坏或格式错误", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存密钥到文件(通用方法)
|
||||
*/
|
||||
private void saveKey(String fileName, byte[] keyBytes) throws IOException {
|
||||
Objects.requireNonNull(keyBytes, "密钥字节数据不可为空");
|
||||
File dir = new File(keyPath);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("创建密钥目录失败:" + keyPath);
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyPath + fileName);
|
||||
fos.write(keyBytes);
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数组(Java 7 兼容)
|
||||
*/
|
||||
private byte[] readFileToBytes(String filePath) throws IOException {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
throw new IOException("文件不存在或为目录:" + filePath);
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
byte[] data = new byte[(int) file.length()];
|
||||
int bytesRead = fis.read(data);
|
||||
if (bytesRead != data.length) {
|
||||
throw new IOException("文件读取不完整");
|
||||
}
|
||||
return data;
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "关闭文件流失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密(带参数校验)
|
||||
*/
|
||||
public byte[] encryptWithPublicKey(String plainText, PublicKey publicKey) throws Exception {
|
||||
Objects.requireNonNull(plainText, "明文不可为空");
|
||||
Objects.requireNonNull(publicKey, "公钥不可为空");
|
||||
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
|
||||
// 检查数据长度是否超过 RSA 限制(2048位密钥最大明文为 214字节,PKCS1Padding)
|
||||
int maxPlainTextSize = cipher.getBlockSize() - 11; // PKCS1Padding 固定填充长度
|
||||
if (plainText.getBytes("UTF-8").length > maxPlainTextSize) {
|
||||
throw new IllegalArgumentException("明文过长,最大支持 " + maxPlainTextSize + " 字节");
|
||||
}
|
||||
|
||||
return cipher.doFinal(plainText.getBytes("UTF-8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密(带参数校验)
|
||||
*/
|
||||
public String decryptWithPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception {
|
||||
Objects.requireNonNull(encryptedData, "密文不可为空");
|
||||
Objects.requireNonNull(privateKey, "私钥不可为空");
|
||||
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedData);
|
||||
return new String(decryptedBytes, "UTF-8");
|
||||
}
|
||||
/**
|
||||
* 将 HTTP 传输的 Base64 字符串还原为加密字节数组(Java 7 兼容)
|
||||
* @param httpString Base64 字符串(非 null)
|
||||
* @return 加密字节数组
|
||||
* @throws IllegalArgumentException 解码失败时抛出
|
||||
*/
|
||||
public byte[] httpStringToEncryptBytes(String httpString) {
|
||||
Objects.requireNonNull(httpString, "HTTP 字符串不可为空");
|
||||
|
||||
// 计算缺失的填充符数量(Java 7 不支持 repeat(),手动拼接)
|
||||
int pad = httpString.length() % 4;
|
||||
StringBuilder paddedString = new StringBuilder(httpString);
|
||||
if (pad != 0) {
|
||||
for (int i = 0; i < pad; i++) {
|
||||
paddedString.append('='); // 补全 '='
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 Base64 解码(Android 原生 Base64 类兼容 Java 7)
|
||||
return Base64.decode(paddedString.toString(), Base64.URL_SAFE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,15 @@ package cc.winboll.studio.winboll.utils;
|
||||
* @Date 2025/06/08 09:05
|
||||
* @Describe Termux 应用操作工具集
|
||||
*/
|
||||
import android.content.Intent;
|
||||
|
||||
public abstract class TermuxUtils {
|
||||
|
||||
public static final String TAG = "TermuxUtils";
|
||||
|
||||
private void runTermuxCommand(String command) {
|
||||
// 1. 创建 Intent,指定 Termux 的 RunCommandService
|
||||
Intent intent = new Intent("com.termux.RUN_COMMAND");
|
||||
/*Intent intent = new Intent("com.termux.RUN_COMMAND");
|
||||
intent.setPackage("com.termux"); // Termux 应用的包名
|
||||
|
||||
// 2. 传递命令参数(必填)
|
||||
@@ -26,6 +28,6 @@ public abstract class TermuxUtils {
|
||||
getApplicationContext().startService(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
package cc.winboll.studio.winboll.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/06/04 17:21
|
||||
* @Describe 应用登录与接口工具
|
||||
*/
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import cc.winboll.studio.winboll.models.UserInfoModel;
|
||||
import cc.winboll.studio.winboll.models.ResponseData;
|
||||
import cc.winboll.studio.libapputils.utils.FileUtils;
|
||||
|
||||
public class YunUtils {
|
||||
public static final String TAG = "YunUtils";
|
||||
// 私有静态实例,类加载时创建
|
||||
private static volatile YunUtils INSTANCE;
|
||||
Context mContext;
|
||||
UserInfoModel mUserInfoModel;
|
||||
String token = "";
|
||||
String mDataFolderPath = "";
|
||||
String mUserInfoModelPath = "";
|
||||
|
||||
private static final int CONNECT_TIMEOUT = 15; // 连接超时时间(秒)
|
||||
private static final int READ_TIMEOUT = 20; // 读取超时时间(秒)
|
||||
private static volatile YunUtils instance;
|
||||
private OkHttpClient okHttpClient;
|
||||
private Handler mainHandler; // 主线程 Handler
|
||||
|
||||
// 私有构造方法,防止外部实例化
|
||||
private YunUtils(Context context) {
|
||||
LogUtils.d(TAG, "YunUtils");
|
||||
mContext = context;
|
||||
mDataFolderPath = mContext.getExternalFilesDir(TAG).toString();
|
||||
File fTest = new File(mDataFolderPath);
|
||||
if (!fTest.exists()) {
|
||||
fTest.mkdirs();
|
||||
}
|
||||
mUserInfoModelPath = mDataFolderPath + File.separator + "UserInfoModel.rsajson";
|
||||
|
||||
okHttpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
.build();
|
||||
mainHandler = new Handler(Looper.getMainLooper()); // 获取主线程 Looper
|
||||
}
|
||||
|
||||
// 公共静态方法,返回唯一实例
|
||||
public static synchronized YunUtils getInstance(Context context) {
|
||||
LogUtils.d(TAG, "getInstance");
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new YunUtils(context);
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void checkLoginStatus() {
|
||||
String token = getLocalToken();
|
||||
LogUtils.d(TAG, String.format("checkLoginStatus token is %s", token));
|
||||
}
|
||||
|
||||
String getLocalToken() {
|
||||
UserInfoModel userInfoModel = loadUserInfoModel();
|
||||
return (userInfoModel == null) ?"": userInfoModel.getToken();
|
||||
}
|
||||
|
||||
public void login(String host, UserInfoModel userInfoModel) {
|
||||
LogUtils.d(TAG, "login");
|
||||
|
||||
// 发送 POST 请求
|
||||
String apiUrl = host + "/login/index.php";
|
||||
// 序列化对象为JSON
|
||||
Gson gson = new Gson();
|
||||
String jsonData = gson.toJson(userInfoModel); // 自动生成标准JSON
|
||||
//String jsonData = userInfoModel.toString();
|
||||
LogUtils.d(TAG, "要发送的数据 : " + jsonData);
|
||||
|
||||
sendPostRequest(apiUrl, jsonData, new OnResponseListener() {
|
||||
// 成功回调(主线程)
|
||||
@Override
|
||||
public void onSuccess(String responseBody) {
|
||||
LogUtils.d(TAG, "onSuccess");
|
||||
LogUtils.d(TAG, String.format("responseBody %s", responseBody));
|
||||
Gson gson = new Gson();
|
||||
ResponseData result = gson.fromJson(responseBody, ResponseData.class); // 转为 Result 实例
|
||||
if(result.getStatus().equals(ResponseData.STATUS_SUCCESS)) {
|
||||
|
||||
UserInfoModel userInfoModel = result.getData();
|
||||
if (userInfoModel != null) {
|
||||
LogUtils.d(TAG, "收到网站 UserInfoModel");
|
||||
String token = userInfoModel.getToken();
|
||||
saveLocalToken(token);
|
||||
checkLoginStatus();
|
||||
}
|
||||
|
||||
} else if(result.getStatus().equals(ResponseData.STATUS_ERROR)) {
|
||||
try {
|
||||
String decodedMessage = URLDecoder.decode(result.getMessage(), "UTF-8");
|
||||
LogUtils.d(TAG, "服务器返回信息: " + decodedMessage);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 失败回调(主线程)
|
||||
@Override
|
||||
public void onFailure(String errorMsg) {
|
||||
LogUtils.d(TAG, errorMsg);
|
||||
// 处理错误
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void saveLocalToken(String token) {
|
||||
UserInfoModel userInfoModel = new UserInfoModel();
|
||||
userInfoModel.setToken(token);
|
||||
saveUserInfoModel(userInfoModel);
|
||||
}
|
||||
|
||||
UserInfoModel loadUserInfoModel() {
|
||||
LogUtils.d(TAG, "loadUserInfoModel");
|
||||
if (new File(mUserInfoModelPath).exists()) {
|
||||
try {
|
||||
// 加载加密后的模型数据
|
||||
byte[] encryptedData = FileUtils.readByteArrayFromFile(mUserInfoModelPath);
|
||||
// 加载 RSA 工具
|
||||
RSAUtils utils = RSAUtils.getInstance(mContext);
|
||||
KeyPair keyPair = utils.getOrGenerateKeys();
|
||||
//PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
// 私钥解密模型数据
|
||||
String szInfo = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
|
||||
LogUtils.d(TAG, String.format("szInfo %s", szInfo));
|
||||
mUserInfoModel = UserInfoModel.parseStringToBean(szInfo, UserInfoModel.class);
|
||||
if (mUserInfoModel == null) {
|
||||
LogUtils.d(TAG, "模型数据解析为空数据。");
|
||||
}
|
||||
LogUtils.d(TAG, "UserInfoModel 解密加载结束。");
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
|
||||
}
|
||||
} else {
|
||||
LogUtils.d(TAG, "云服务登录信息不存在。");
|
||||
mUserInfoModel = null;
|
||||
}
|
||||
return mUserInfoModel;
|
||||
}
|
||||
|
||||
void saveUserInfoModel(UserInfoModel userInfoModel) {
|
||||
LogUtils.d(TAG, "saveUserInfoModel");
|
||||
try {
|
||||
String szInfo = userInfoModel.toString();
|
||||
LogUtils.d(TAG, "原始数据: " + szInfo);
|
||||
|
||||
RSAUtils utils = RSAUtils.getInstance(mContext);
|
||||
KeyPair keyPair = utils.getOrGenerateKeys();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
// 公钥加密(传入字节数组,避免中间字符串转换)
|
||||
byte[] encryptedData = utils.encryptWithPublicKey(szInfo, publicKey);
|
||||
|
||||
// 保存加密字节数组到文件(直接操作字节,无需转字符串)
|
||||
FileUtils.writeByteArrayToFile(encryptedData, mUserInfoModelPath);
|
||||
LogUtils.d(TAG, "加密数据已保存");
|
||||
|
||||
// 测试解密(仅调试用)
|
||||
String szInfo2 = utils.decryptWithPrivateKey(encryptedData, keyPair.getPrivate());
|
||||
LogUtils.d(TAG, "解密结果: " + szInfo2);
|
||||
|
||||
mUserInfoModel = UserInfoModel.parseStringToBean(szInfo2, UserInfoModel.class);
|
||||
if (mUserInfoModel == null) {
|
||||
LogUtils.d(TAG, "模型解析失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtils.d(TAG, "加密/解密失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 发送 POST 请求(JSON 数据)
|
||||
public void sendPostRequest(String url, String data, OnResponseListener listener) {
|
||||
RequestBody requestBody = RequestBody.create(
|
||||
MediaType.parse("application/json; charset=utf-8"), // 关键头信息
|
||||
data.getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(requestBody)
|
||||
.addHeader("Content-Type", "application/json") // 显式添加头
|
||||
.build();
|
||||
|
||||
executeRequest(request, listener);
|
||||
}
|
||||
|
||||
// 发送 GET 请求
|
||||
public void sendGetRequest(String url, OnResponseListener listener) {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build();
|
||||
executeRequest(request, listener);
|
||||
}
|
||||
|
||||
// 执行请求(子线程处理)
|
||||
private void executeRequest(final Request request, final OnResponseListener listener) {
|
||||
okHttpClient.newCall(request).enqueue(new Callback() {
|
||||
// 响应成功(子线程)
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
try {
|
||||
if (!response.isSuccessful()) {
|
||||
postFailure(listener, "响应码错误:" + response.code());
|
||||
return;
|
||||
}
|
||||
String responseBody = response.body().string();
|
||||
postSuccess(listener, responseBody);
|
||||
} catch (Exception e) {
|
||||
postFailure(listener, "解析失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 响应失败(子线程)
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
postFailure(listener, "网络失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
// 主线程回调(使用 Handler)
|
||||
private void postSuccess(final OnResponseListener listener, final String msg) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onSuccess(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void postFailure(final OnResponseListener listener, final String msg) {
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onFailure(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnResponseListener {
|
||||
/**
|
||||
* 成功响应(主线程回调)
|
||||
* @param responseBody 响应体字符串
|
||||
*/
|
||||
void onSuccess(String responseBody);
|
||||
|
||||
/**
|
||||
* 失败回调(包含错误信息)
|
||||
* @param errorMsg 错误描述
|
||||
*/
|
||||
void onFailure(String errorMsg);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.widgets;
|
||||
package cc.winboll.studio.winboll.widgets;
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2025/02/15 14:41:25
|
||||
@@ -11,18 +11,17 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.RemoteViews;
|
||||
import cc.winboll.studio.appbase.R;
|
||||
import cc.winboll.studio.appbase.receivers.APPNewsWidgetClickListener;
|
||||
import cc.winboll.studio.libappbase.AppUtils;
|
||||
import cc.winboll.studio.libaes.models.WinBoLLModel;
|
||||
import cc.winboll.studio.libaes.winboll.WinBoLL;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.models.APPModel;
|
||||
import cc.winboll.studio.libappbase.models.WinBoLLNewsBean;
|
||||
import cc.winboll.studio.libappbase.winboll.WinBoLL;
|
||||
import cc.winboll.studio.libapputils.utils.AppUtils;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.winboll.models.WinBoLLNewsBean;
|
||||
import cc.winboll.studio.winboll.receivers.APPNewsWidgetClickListener;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import cc.winboll.studio.libappbase.models.WinBoLLModel;
|
||||
|
||||
public class APPNewsWidget extends AppWidgetProvider {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.appbase.widgets;
|
||||
package cc.winboll.studio.winboll.widgets;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -11,12 +11,8 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.RemoteViews;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.R;
|
||||
import cc.winboll.studio.libappbase.utils.ServiceUtils;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||
import cc.winboll.studio.winboll.R;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
public class StatusWidget extends AppWidgetProvider {
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cc.winboll.studio.libappbase.widgets;
|
||||
package cc.winboll.studio.winboll.widgets;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -9,7 +9,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.utils.ToastUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
public class StatusWidgetClickListener extends BroadcastReceiver {
|
||||
|
||||
|
||||
11
winboll/src/main/res/drawable/ic_cloud.xml
Normal file
11
winboll/src/main/res/drawable/ic_cloud.xml
Normal 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="#ff000000"
|
||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20Z"/>
|
||||
|
||||
</vector>
|
||||
11
winboll/src/main/res/drawable/ic_cloud_outline.xml
Normal file
11
winboll/src/main/res/drawable/ic_cloud_outline.xml
Normal 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="#ff000000"
|
||||
android:pathData="M6.5,20Q4.22,20 2.61,18.43 1,16.85 1,14.58 1,12.63 2.17,11.1 3.35,9.57 5.25,9.15 5.88,6.85 7.75,5.43 9.63,4 12,4 14.93,4 16.96,6.04 19,8.07 19,11 20.73,11.2 21.86,12.5 23,13.78 23,15.5 23,17.38 21.69,18.69 20.38,20 18.5,20M6.5,18H18.5Q19.55,18 20.27,17.27 21,16.55 21,15.5 21,14.45 20.27,13.73 19.55,13 18.5,13H17V11Q17,8.93 15.54,7.46 14.08,6 12,6 9.93,6 8.46,7.46 7,8.93 7,11H6.5Q5.05,11 4.03,12.03 3,13.05 3,14.5 3,15.95 4.03,17 5.05,18 6.5,18M12,12Z"/>
|
||||
|
||||
</vector>
|
||||
Reference in New Issue
Block a user