diff --git a/positions/README.md b/positions/README.md
new file mode 100644
index 0000000..6acd7f1
--- /dev/null
+++ b/positions/README.md
@@ -0,0 +1,38 @@
+# Positions
+
+#### 介绍
+位置应用,与卫星定位有关的应用。可以根据设定的位置与时间条件判断,来发送通知的应用。
+
+#### 软件架构
+适配安卓应用 [AIDE Pro] 的 Gradle 编译结构。
+也适配安卓应用 [AndroidIDE] 的 Gradle 编译结构。
+
+
+#### Gradle 编译说明
+调试版编译命令 :gradle assembleBetaDebug
+阶段版编译命令 :gradle assembleStageRelease
+
+#### 使用说明
+
+在安卓系统中需要设置两个权限允许。
+1.自启动权限允许。
+2.省电策略-无限制权限允许。
+
+#### 参与贡献
+
+1. Fork 本仓库
+2. 新建 Feat_xxx 分支
+3. 提交代码 : ZhanGSKen(ZhanGSKen@AliYun.Com)
+4. 新建 Pull Request
+
+
+#### 特技
+
+1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
+3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
+4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
+5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+
+#### 参考文档
diff --git a/positions/app_update_description.txt b/positions/app_update_description.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/positions/app_update_description.txt
@@ -0,0 +1 @@
+
diff --git a/positions/build.gradle b/positions/build.gradle
new file mode 100644
index 0000000..6d015af
--- /dev/null
+++ b/positions/build.gradle
@@ -0,0 +1,71 @@
+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.positions"
+ minSdkVersion 21
+ targetSdkVersion 30
+ versionCode 1
+ // versionName 更新后需要手动设置
+ // 项目模块目录的 build.gradle 文件的 stageCount=0
+ // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
+ versionName "1.0"
+ if(true) {
+ versionName = genVersionName("${versionName}")
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+}
+
+dependencies {
+ // 二维码使用的类库
+ api 'com.google.zxing:core:3.4.1'
+ api 'com.journeyapps:zxing-android-embedded:3.6.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.1.0'
+ api 'androidx.viewpager:viewpager:1.0.0'
+ api 'androidx.fragment:fragment:1.1.0'
+ api 'com.google.android.material:material:1.4.0'
+
+ api 'cc.winboll.studio:libapputils:9.3.2'
+ api 'cc.winboll.studio:libappbase:1.5.6'
+
+ api fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/positions/build.properties b/positions/build.properties
new file mode 100644
index 0000000..c76c4df
--- /dev/null
+++ b/positions/build.properties
@@ -0,0 +1,8 @@
+#Created by .winboll/winboll_app_build.gradle
+#Fri Feb 21 17:45:58 GMT 2025
+stageCount=0
+libraryProject=
+baseVersion=1.0
+publishVersion=1.0.0
+buildCount=1
+baseBetaVersion=1.0.1
diff --git a/positions/proguard-rules.pro b/positions/proguard-rules.pro
new file mode 100644
index 0000000..233bad2
--- /dev/null
+++ b/positions/proguard-rules.pro
@@ -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 *;
+#}
diff --git a/positions/src/beta/AndroidManifest.xml b/positions/src/beta/AndroidManifest.xml
new file mode 100644
index 0000000..c598f4f
--- /dev/null
+++ b/positions/src/beta/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/beta/res/values/strings.xml b/positions/src/beta/res/values/strings.xml
new file mode 100644
index 0000000..7ef2ccf
--- /dev/null
+++ b/positions/src/beta/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ Positions+
+
+
diff --git a/positions/src/main/AndroidManifest.xml b/positions/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cbe3d34
--- /dev/null
+++ b/positions/src/main/AndroidManifest.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/java/cc/winboll/studio/positions/App.java b/positions/src/main/java/cc/winboll/studio/positions/App.java
new file mode 100644
index 0000000..0782b21
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/App.java
@@ -0,0 +1,28 @@
+package cc.winboll.studio.positions;
+
+/**
+ * @Author ZhanGSKen@QQ.COM
+ * @Date 2024/12/08 15:10:51
+ * @Describe 全局应用类
+ */
+import cc.winboll.studio.libappbase.GlobalApplication;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libapputils.app.WinBollActivityManager;
+
+public class App extends GlobalApplication {
+
+ public static final String TAG = "App";
+
+ @Override
+ public void onCreate() {
+ // 必须在调用基类前设置应用调试标志,
+ // 这样可以预先设置日志与数据的存储根目录。
+ setIsDebuging(this, BuildConfig.DEBUG);
+ super.onCreate();
+ // 设置 WinBoll 应用 UI 类型
+ WinBollActivityManager.getInstance(this).setWinBollUI_TYPE(WinBollActivityManager.WinBollUI_TYPE.Aplication);
+
+ LogUtils.d(TAG, "onCreate");
+ }
+
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java
new file mode 100644
index 0000000..0732632
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java
@@ -0,0 +1,387 @@
+package cc.winboll.studio.positions;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.viewpager.widget.ViewPager;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.LogView;
+import cc.winboll.studio.libapputils.app.IWinBollActivity;
+import cc.winboll.studio.libapputils.app.WinBollActivityManager;
+import cc.winboll.studio.libapputils.bean.APPInfo;
+import cc.winboll.studio.libapputils.view.YesNoAlertDialog;
+import cc.winboll.studio.positions.R;
+import cc.winboll.studio.positions.activities.SettingsActivity;
+import cc.winboll.studio.positions.adapters.MyPagerAdapter;
+import cc.winboll.studio.positions.beans.MainServiceBean;
+import cc.winboll.studio.positions.services.MainService;
+import com.google.android.material.tabs.TabLayout;
+import java.util.ArrayList;
+import java.util.List;
+
+final public class MainActivity extends AppCompatActivity implements IWinBollActivity, ViewPager.OnPageChangeListener, View.OnClickListener {
+
+ public static final String TAG = "MainActivity";
+
+ public static final int REQUEST_HOME_ACTIVITY = 0;
+ public static final int REQUEST_ABOUT_ACTIVITY = 1;
+
+ public static final String ACTION_SOS = "cc.winboll.studio.libappbase.WinBoll.ACTION_SOS";
+
+ LogView mLogView;
+ Toolbar mToolbar;
+ CheckBox cbMainService;
+ MainServiceBean mMainServiceBean;
+ ViewPager viewPager;
+ private List views; //用来存放放进ViewPager里面的布局
+ //实例化存储imageView(导航原点)的集合
+ ImageView[] imageViews;
+ //MyPagerAdapter adapter;//适配器
+ MyPagerAdapter pagerAdapter;
+ LinearLayout linearLayout;//下标所在在LinearLayout布局里
+ int currentPoint = 0;//当前被选中中页面的下标
+
+ private static final int DIALER_REQUEST_CODE = 1;
+
+ @Override
+ public AppCompatActivity getActivity() {
+ return this;
+ }
+
+ @Override
+ public APPInfo getAppInfo() {
+// String szBranchName = "contacts";
+//
+// APPInfo appInfo = AboutActivityFactory.buildDefaultAPPInfo();
+// appInfo.setAppName("Contacts");
+// appInfo.setAppIcon(cc.winboll.studio.libapputils.R.drawable.ic_winboll);
+// appInfo.setAppDescription("Contacts Description");
+// appInfo.setAppGitName("APP");
+// appInfo.setAppGitOwner("Studio");
+// appInfo.setAppGitAPPBranch(szBranchName);
+// appInfo.setAppGitAPPSubProjectFolder(szBranchName);
+// appInfo.setAppHomePage("https://www.winboll.cc/studio/details.php?app=Contacts");
+// appInfo.setAppAPKName("Contacts");
+// appInfo.setAppAPKFolderName("Contacts");
+// return appInfo;
+ return null;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // 接收并处理 Intent 数据,函数 Intent 处理接收就直接返回
+ //if (prosessIntents(getIntent())) return;
+ // 以下正常创建主窗口
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // 初始化工具栏
+ mToolbar = findViewById(R.id.activitymainToolbar1);
+ setSupportActionBar(mToolbar);
+ if (isEnableDisplayHomeAsUp()) {
+ // 显示后退按钮
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ getSupportActionBar().setSubtitle(getTag());
+
+ initData();
+ initView();
+ //initPoint();//调用初始化导航原点的方法
+ viewPager.addOnPageChangeListener(this);//滑动事件
+
+ ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
+ MyPagerAdapter pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(pagerAdapter);
+ TabLayout tabLayout = findViewById(R.id.activitymainTabLayout1);
+ tabLayout.setupWithViewPager(viewPager);
+
+// mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+// if (mMainServiceBean == null) {
+// mMainServiceBean = new MainServiceBean();
+// }
+// cbMainService = findViewById(R.id.activitymainCheckBox1);
+// cbMainService.setChecked(mMainServiceBean.isEnable());
+// cbMainService.setOnClickListener(new View.OnClickListener(){
+// @Override
+// public void onClick(View view) {
+// if (cbMainService.isChecked()) {
+// MainService.startMainService(MainActivity.this);
+// } else {
+// MainService.stopMainService(MainActivity.this);
+// }
+// }
+// });
+ MainService.startMainService(MainActivity.this);
+ }
+
+ //初始化view,即显示的图片
+ void initView() {
+ viewPager = findViewById(R.id.activitymainViewPager1);
+ pagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(pagerAdapter);
+ //adapter = new MyPagerAdapter(views);
+ //viewPager = findViewById(R.id.activitymainViewPager1);
+ //viewPager.setAdapter(adapter);
+ //linearLayout = findViewById(R.id.activitymainLinearLayout1);
+ //initPoint();//初始化页面下方的点
+ viewPager.setOnPageChangeListener(this);
+
+ }
+
+ //初始化所要显示的布局
+ void initData() {
+ ViewPager viewPager = findViewById(R.id.activitymainViewPager1);
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view1 = inflater.inflate(R.layout.fragment_call, viewPager, false);
+ View view2 = inflater.inflate(R.layout.fragment_contacts, viewPager, false);
+ View view3 = inflater.inflate(R.layout.fragment_log, viewPager, false);
+
+ views = new ArrayList<>();
+ views.add(view1);
+ views.add(view2);
+ views.add(view3);
+ }
+
+// void initPoint() {
+// imageViews = new ImageView[5];//实例化5个图片
+// for (int i = 0; i < linearLayout.getChildCount(); i++) {
+// imageViews[i] = (ImageView) linearLayout.getChildAt(i);
+// imageViews[i].setImageResource(R.drawable.ic_launcher);
+// imageViews[i].setOnClickListener(this);//点击导航点,即可跳转
+// imageViews[i].setTag(i);//重复利用实例化的对象
+// }
+// currentPoint = 0;//默认第一个坐标
+// imageViews[currentPoint].setImageResource(R.drawable.ic_launcher);
+// }
+
+ //OnPageChangeListener接口要实现的三个方法
+ /* onPageScrollStateChanged(int state)
+ 此方法是在状态改变的时候调用,其中state这个参数有三种状态:
+ SCROLL_STATE_DRAGGING(1)表示用户手指“按在屏幕上并且开始拖动”的状态
+ (手指按下但是还没有拖动的时候还不是这个状态,只有按下并且手指开始拖动后log才打出。)
+ SCROLL_STATE_IDLE(0)滑动动画做完的状态。
+ SCROLL_STATE_SETTLING(2)在“手指离开屏幕”的状态。*/
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ /* onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
+ 当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
+
+ position :当前页面,即你点击滑动的页面(从A滑B,则是A页面的position。
+ positionOffset:当前页面偏移的百分比
+ positionOffsetPixels:当前页面偏移的像素位置*/
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+ /* onPageSelected(int position)
+ 此方法是页面滑动完后得到调用,position是你当前选中的页面的Position(位置编号)
+ (从A滑动到B,就是B的position)*/
+ public void onPageSelected(int position) {
+
+// ImageView preView = imageViews[currentPoint];
+// preView.setImageResource(R.drawable.ic_launcher);
+// ImageView currView = imageViews[position];
+// currView.setImageResource(R.drawable.ic_launcher);
+// currentPoint = position;
+ }
+
+ //小圆点点击事件
+ @Override
+ public void onClick(View v) {
+ // TODO Auto-generated method stub
+ //通过getTag(),可以判断是哪个控件
+// int i = (Integer) v.getTag();
+// viewPager.setCurrentItem(i);//直接跳转到某一个页面的情况
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ //setSubTitle("");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ LogUtils.d(TAG, "onDestroy() SOS");
+ }
+
+
+
+ //
+ // 处理传入的 Intent 数据
+ //
+// boolean prosessIntents(Intent intent) {
+// if (intent == null
+// || intent.getAction() == null
+// || intent.getAction().equals(""))
+// return false;
+//
+// if (intent.getAction().equals(StringToQrCodeView.ACTION_UNITTEST_QRCODE)) {
+// try {
+// WinBollActivity clazzActivity = UnitTestActivity.class.newInstance();
+// String tag = clazzActivity.getTag();
+// LogUtils.d(TAG, "String tag = clazzActivity.getTag(); tag " + tag);
+// Intent subIntent = new Intent(this, UnitTestActivity.class);
+// subIntent.setAction(intent.getAction());
+// File file = new File(getCacheDir(), UUID.randomUUID().toString());
+// //取出文件uri
+// Uri uri = intent.getData();
+// if (uri == null) {
+// uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
+// }
+// //获取文件真实地址
+// String szSrcPath = UriUtils.getFileFromUri(getApplication(), uri);
+// if (TextUtils.isEmpty(szSrcPath)) {
+// return false;
+// }
+//
+// Files.copy(Paths.get(szSrcPath), Paths.get(file.getPath()));
+// //startWinBollActivity(subIntent, tag);
+// WinBollActivityManager.getInstance(this).startWinBollActivity(this, subIntent, UnitTestActivity.class);
+// } catch (IllegalAccessException | InstantiationException | IOException e) {
+// LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+// // 函数处理异常返回失败
+// return false;
+// }
+// } else {
+// LogUtils.d(TAG, "prosessIntents|" + intent.getAction() + "|yet");
+// return false;
+// }
+// return true;
+// }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ public Toolbar initToolBar() {
+ return findViewById(R.id.activitymainToolbar1);
+ }
+
+ @Override
+ public boolean isAddWinBollToolBar() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnableDisplayHomeAsUp() {
+ return false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ exit();
+ }
+
+ void exit() {
+ YesNoAlertDialog.OnDialogResultListener listener = new YesNoAlertDialog.OnDialogResultListener(){
+
+ @Override
+ public void onYes() {
+ WinBollActivityManager.getInstance(getApplicationContext()).finishAll();
+ }
+
+ @Override
+ public void onNo() {
+ }
+ };
+ YesNoAlertDialog.show(this, "[ " + getString(R.string.app_name) + " ]", "Exit(Yes/No).\nIs close all activity?", listener);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.toolbar_main, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.item_settings) {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
+ }
+// } else
+// if (item.getItemId() == R.id.item_exit) {
+// exit();
+// return true;
+// }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ /**
+ * Android M 及以上检查是否是系统默认电话应用
+ */
+ public boolean isDefaultPhoneCallApp() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);
+ if (manger != null && manger.getDefaultDialerPackage() != null) {
+ return manger.getDefaultDialerPackage().equals(getPackageName());
+ }
+ }
+ return false;
+ }
+
+ public boolean isServiceRunning(Class> serviceClass) {
+ ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ if (manager == null) return false;
+
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
+ Integer.MAX_VALUE)) {
+ if (serviceClass.getName().equals(service.service.getClassName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+// switch (resultCode) {
+// case REQUEST_HOME_ACTIVITY : {
+// LogUtils.d(TAG, "REQUEST_HOME_ACTIVITY");
+// break;
+// }
+// case REQUEST_ABOUT_ACTIVITY : {
+// LogUtils.d(TAG, "REQUEST_ABOUT_ACTIVITY");
+// break;
+// }
+// default : {
+// super.onActivityResult(requestCode, resultCode, data);
+// }
+// }
+ if (requestCode == DIALER_REQUEST_CODE) {
+ if (resultCode == Activity.RESULT_OK) {
+ Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/activities/SettingsActivity.java b/positions/src/main/java/cc/winboll/studio/positions/activities/SettingsActivity.java
new file mode 100644
index 0000000..5639791
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/activities/SettingsActivity.java
@@ -0,0 +1,138 @@
+package cc.winboll.studio.positions.activities;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/21 05:37:42
+ */
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import cc.winboll.studio.positions.R;
+import com.hjq.toast.ToastUtils;
+import java.lang.reflect.Field;
+import androidx.appcompat.widget.Toolbar;
+import cc.winboll.studio.libappbase.IWinBollActivity;
+import cc.winboll.studio.libappbase.bean.APPInfo;
+
+public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
+
+ public static final String TAG = "SettingsActivity";
+
+ Toolbar mToolbar;
+
+ @Override
+ public APPInfo getAppInfo() {
+ return null;
+ }
+
+ @Override
+ public AppCompatActivity getActivity() {
+ return this;
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ public Toolbar initToolBar() {
+ return findViewById(R.id.activitymainToolbar1);
+ }
+
+ @Override
+ public boolean isAddWinBollToolBar() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnableDisplayHomeAsUp() {
+ return false;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ // 初始化工具栏
+ mToolbar = findViewById(R.id.activitymainToolbar1);
+ setSupportActionBar(mToolbar);
+ if (isEnableDisplayHomeAsUp()) {
+ // 显示后退按钮
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ getSupportActionBar().setSubtitle(getTag());
+
+ }
+
+ public void onDefaultPhone(View view) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
+ startActivity(intent);
+ }
+ public void onCanDrawOverlays(View view) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && !Settings.canDrawOverlays(this)) {
+ // 请求 悬浮框 权限
+ askForDrawOverlay();
+ } else {
+ ToastUtils.show("悬浮窗已开启");
+ }
+ }
+
+ private void askForDrawOverlay() {
+ AlertDialog alertDialog = new AlertDialog.Builder(this)
+ .setTitle("允许显示悬浮框")
+ .setMessage("为了使电话监听服务正常工作,请允许这项权限")
+ .setPositiveButton("去设置", new DialogInterface.OnClickListener(){
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ openDrawOverlaySettings();
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("稍后再说", new DialogInterface.OnClickListener(){
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .create();
+
+ //noinspection ConstantConditions
+ alertDialog.getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ alertDialog.show();
+ }
+
+ /**
+ * 跳转悬浮窗管理设置界面
+ */
+ private void openDrawOverlaySettings() {
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // Android M 以上引导用户去系统设置中打开允许悬浮窗
+ // 使用反射是为了用尽可能少的代码保证在大部分机型上都可用
+ try {
+ Context context = this;
+ Class clazz = Settings.class;
+ Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
+ Intent intent = new Intent(field.get(null).toString());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("package:" + context.getPackageName()));
+ context.startActivity(intent);
+ } catch (Exception e) {
+ Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/adapters/MyPagerAdapter.java b/positions/src/main/java/cc/winboll/studio/positions/adapters/MyPagerAdapter.java
new file mode 100644
index 0000000..f668734
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/adapters/MyPagerAdapter.java
@@ -0,0 +1,42 @@
+package cc.winboll.studio.positions.adapters;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 13:33:04
+ * @Describe MyPagerAdapter
+ */
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import cc.winboll.studio.positions.fragments.CallFragment;
+import cc.winboll.studio.positions.fragments.ContactsFragment;
+import cc.winboll.studio.positions.fragments.LogFragment;
+
+public class MyPagerAdapter extends FragmentPagerAdapter {
+ public static final String TAG = "MyPagerAdapter";
+
+ private static final int PAGE_COUNT = 3;
+
+ public MyPagerAdapter(@NonNull FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ if(position == 1) {
+ return ContactsFragment.newInstance(position);
+ } else if(position == 2) {
+ return LogFragment.newInstance(position);
+ } else {
+ return CallFragment.newInstance(position);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return PAGE_COUNT;
+ }
+}
+
diff --git a/positions/src/main/java/cc/winboll/studio/positions/beans/MainServiceBean.java b/positions/src/main/java/cc/winboll/studio/positions/beans/MainServiceBean.java
new file mode 100644
index 0000000..7c40d6f
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/beans/MainServiceBean.java
@@ -0,0 +1,68 @@
+package cc.winboll.studio.positions.beans;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 07:06:13
+ */
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import cc.winboll.studio.libappbase.BaseBean;
+import java.io.IOException;
+
+public class MainServiceBean extends BaseBean {
+
+ public static final String TAG = "MainServiceBean";
+
+ boolean isEnable;
+
+ public MainServiceBean() {
+ this.isEnable = false;
+ }
+
+ public void setIsEnable(boolean isEnable) {
+ this.isEnable = isEnable;
+ }
+
+ public boolean isEnable() {
+ return isEnable;
+ }
+
+ @Override
+ public String getName() {
+ return MainServiceBean.class.getName();
+ }
+
+ @Override
+ public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
+ super.writeThisToJsonWriter(jsonWriter);
+ MainServiceBean bean = this;
+ jsonWriter.name("isEnable").value(bean.isEnable());
+
+ }
+
+ @Override
+ public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
+ if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
+ if (name.equals("isEnable")) {
+ setIsEnable(jsonReader.nextBoolean());
+ } 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;
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/fragments/CallFragment.java b/positions/src/main/java/cc/winboll/studio/positions/fragments/CallFragment.java
new file mode 100644
index 0000000..72f98b3
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/fragments/CallFragment.java
@@ -0,0 +1,51 @@
+package cc.winboll.studio.positions.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:57:00
+ * @Describe 拨号
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.positions.R;
+import cc.winboll.studio.libappbase.LogView;
+import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
+import android.widget.TextView;
+
+public class CallFragment extends Fragment {
+
+ public static final String TAG = "CallFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static CallFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ CallFragment fragment = new CallFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments()!= null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_call, container, false);
+ TextView textView = view.findViewById(R.id.page_text);
+ textView.setText("这是第 " + mPage + " 页");
+ return view;
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/fragments/ContactsFragment.java b/positions/src/main/java/cc/winboll/studio/positions/fragments/ContactsFragment.java
new file mode 100644
index 0000000..16b3880
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/fragments/ContactsFragment.java
@@ -0,0 +1,50 @@
+package cc.winboll.studio.positions.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:57:50
+ * @Describe 联系人
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.positions.R;
+
+public class ContactsFragment extends Fragment {
+
+ public static final String TAG = "ContactsFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static ContactsFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ ContactsFragment fragment = new ContactsFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments()!= null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_contacts, container, false);
+ TextView textView = view.findViewById(R.id.page_text);
+ textView.setText("这是第 " + mPage + " 页");
+ return view;
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/fragments/LogFragment.java b/positions/src/main/java/cc/winboll/studio/positions/fragments/LogFragment.java
new file mode 100644
index 0000000..491cc40
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/fragments/LogFragment.java
@@ -0,0 +1,50 @@
+package cc.winboll.studio.positions.fragments;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/20 12:58:15
+ * @Describe 应用日志
+ */
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import cc.winboll.studio.positions.R;
+import cc.winboll.studio.libappbase.LogView;
+
+public class LogFragment extends Fragment {
+
+ public static final String TAG = "LogFragment";
+
+ private static final String ARG_PAGE = "ARG_PAGE";
+ private int mPage;
+
+ public static LogFragment newInstance(int page) {
+ Bundle args = new Bundle();
+ args.putInt(ARG_PAGE, page);
+ LogFragment fragment = new LogFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mPage = getArguments().getInt(ARG_PAGE);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_log, container, false);
+ LogView logView = view.findViewById(R.id.logview);
+ logView.start();
+ return view;
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/handlers/MainServiceHandler.java b/positions/src/main/java/cc/winboll/studio/positions/handlers/MainServiceHandler.java
new file mode 100644
index 0000000..d4c6030
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/handlers/MainServiceHandler.java
@@ -0,0 +1,38 @@
+package cc.winboll.studio.positions.handlers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:51:40
+ */
+import android.os.Handler;
+import android.os.Message;
+import cc.winboll.studio.positions.services.MainService;
+import java.lang.ref.WeakReference;
+
+public class MainServiceHandler extends Handler {
+ public static final String TAG = "MainServiceHandler";
+
+ public static final int MSG_REMINDTHREAD = 0;
+
+ WeakReference serviceWeakReference;
+ public MainServiceHandler(MainService service) {
+ serviceWeakReference = new WeakReference(service);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REMINDTHREAD: // 处理下载完成消息,更新UI
+ {
+ // 显示提醒消息
+ //
+ //LogUtils.d(TAG, "显示提醒消息");
+ MainService mainService = serviceWeakReference.get();
+ if (mainService != null) {
+ mainService.appenMessage((String)msg.obj);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/receivers/MainReceiver.java b/positions/src/main/java/cc/winboll/studio/positions/receivers/MainReceiver.java
new file mode 100644
index 0000000..d3daaa5
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/receivers/MainReceiver.java
@@ -0,0 +1,49 @@
+package cc.winboll.studio.positions.receivers;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 06:58:04
+ * @Describe 主要广播接收器
+ */
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import cc.winboll.studio.positions.services.MainService;
+import com.hjq.toast.ToastUtils;
+import java.lang.ref.WeakReference;
+
+public class MainReceiver extends BroadcastReceiver {
+
+ public static final String TAG = "MainReceiver";
+ public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
+ WeakReference mwrService;
+ // 存储电量指示值,
+ // 用于校验电量消息时的电量变化
+ static volatile int _mnTheQuantityOfElectricityOld = -1;
+ static volatile boolean _mIsCharging = false;
+
+ public MainReceiver(MainService service) {
+ mwrService = new WeakReference(service);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String szAction = intent.getAction();
+ if (szAction.equals(ACTION_BOOT_COMPLETED)) {
+ ToastUtils.show("ACTION_BOOT_COMPLETED");
+ MainService.startMainService(context);
+ } else {
+ ToastUtils.show(szAction);
+ }
+ }
+
+ // 注册 Receiver
+ //
+ public void registerAction(Context context) {
+ IntentFilter filter=new IntentFilter();
+ filter.addAction(ACTION_BOOT_COMPLETED);
+ //filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ context.registerReceiver(this, filter);
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/AssistantService.java b/positions/src/main/java/cc/winboll/studio/positions/services/AssistantService.java
new file mode 100644
index 0000000..192d8c2
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/services/AssistantService.java
@@ -0,0 +1,139 @@
+package cc.winboll.studio.positions.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:38:31
+ * @Describe 守护进程服务
+ */
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import cc.winboll.studio.positions.beans.MainServiceBean;
+import cc.winboll.studio.positions.services.MainService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+
+public class AssistantService extends Service {
+
+ public static final String TAG = "AssistantService";
+
+ MainServiceBean mMainServiceBean;
+ MyServiceConnection mMyServiceConnection;
+ MainService mMainService;
+ boolean isBound = false;
+ volatile boolean isThreadAlive = false;
+
+ public synchronized void setIsThreadAlive(boolean isThreadAlive) {
+ LogUtils.d(TAG, "setIsThreadAlive(...)");
+ LogUtils.d(TAG, String.format("isThreadAlive %s", isThreadAlive));
+ this.isThreadAlive = isThreadAlive;
+ }
+
+ public boolean isThreadAlive() {
+ return isThreadAlive;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ @Override
+ public void onCreate() {
+ LogUtils.d(TAG, "onCreate");
+ super.onCreate();
+
+ //mMyBinder = new MyBinder();
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+ // 设置运行参数
+ setIsThreadAlive(false);
+ assistantService();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "call onStartCommand(...)");
+ assistantService();
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "onDestroy");
+ setIsThreadAlive(false);
+ // 解除绑定
+ if (isBound) {
+ unbindService(mMyServiceConnection);
+ isBound = false;
+ }
+ super.onDestroy();
+ }
+
+ // 运行服务内容
+ //
+ void assistantService() {
+ LogUtils.d(TAG, "assistantService()");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ LogUtils.d(TAG, String.format("mMainServiceBean.isEnable() %s", mMainServiceBean.isEnable()));
+ if (mMainServiceBean.isEnable()) {
+ LogUtils.d(TAG, String.format("mIsThreadAlive %s", isThreadAlive()));
+ if (isThreadAlive() == false) {
+ // 设置运行状态
+ setIsThreadAlive(true);
+ // 唤醒和绑定主进程
+ wakeupAndBindMain();
+ }
+ }
+ }
+
+ // 唤醒和绑定主进程
+ //
+ void wakeupAndBindMain() {
+ LogUtils.d(TAG, "wakeupAndBindMain()");
+ // 绑定服务的Intent
+ Intent intent = new Intent(this, MainService.class);
+ startService(new Intent(this, MainService.class));
+ bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
+
+// startService(new Intent(this, MainService.class));
+// 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, "onServiceConnected(...)");
+ MainService.MyBinder binder = (MainService.MyBinder) service;
+ mMainService = binder.getService();
+ isBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ LogUtils.d(TAG, "onServiceDisconnected(...)");
+ mMainServiceBean = MainServiceBean.loadBean(AssistantService.this, MainServiceBean.class);
+ if (mMainServiceBean.isEnable()) {
+ wakeupAndBindMain();
+ }
+ isBound = false;
+ mMainService = null;
+ }
+ }
+
+ // 用于返回服务实例的Binder
+ public class MyBinder extends Binder {
+ AssistantService getService() {
+ LogUtils.d(TAG, "AssistantService MyBinder getService()");
+ return AssistantService.this;
+ }
+ }
+}
diff --git a/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java
new file mode 100644
index 0000000..1288821
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/services/MainService.java
@@ -0,0 +1,228 @@
+package cc.winboll.studio.positions.services;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/13 06:56:41
+ * @Describe 拨号主服务
+ * 参考:
+ * 进程保活-双进程守护的正确姿势
+ * https://blog.csdn.net/sinat_35159441/article/details/75267380
+ * Android Service之onStartCommand方法研究
+ * https://blog.csdn.net/cyp331203/article/details/38920491
+ */
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+import cc.winboll.studio.positions.beans.MainServiceBean;
+import cc.winboll.studio.positions.handlers.MainServiceHandler;
+import cc.winboll.studio.positions.receivers.MainReceiver;
+import cc.winboll.studio.positions.services.MainService;
+import cc.winboll.studio.positions.threads.MainServiceThread;
+
+public class MainService extends Service {
+
+ public static final String TAG = "MainService";
+
+ public static final int MSG_UPDATE_STATUS = 0;
+
+ static MainService _mControlCenterService;
+
+ volatile boolean isServiceRunning;
+
+ MainServiceBean mMainServiceBean;
+ MainServiceThread mMainServiceThread;
+ MainServiceHandler mMainServiceHandler;
+ MyServiceConnection mMyServiceConnection;
+ AssistantService mAssistantService;
+ boolean isBound = false;
+ MainReceiver mMainReceiver;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ public MainServiceThread getRemindThread() {
+ return mMainServiceThread;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.d(TAG, "onCreate()");
+ _mControlCenterService = MainService.this;
+ isServiceRunning = false;
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+
+ if (mMyServiceConnection == null) {
+ mMyServiceConnection = new MyServiceConnection();
+ }
+ mMainServiceHandler = new MainServiceHandler(this);
+
+ // 运行服务内容
+ mainService();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.d(TAG, "onStartCommand(...)");
+ // 运行服务内容
+ mainService();
+ return (mMainServiceBean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
+ }
+
+ // 运行服务内容
+ //
+ void mainService() {
+ LogUtils.d(TAG, "mainService()");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ if (mMainServiceBean.isEnable() && isServiceRunning == false) {
+ LogUtils.d(TAG, "mainService() start running");
+ isServiceRunning = true;
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+ // 召唤 WinBoll APP 绑定本服务
+ SOS.bindToAPPService(this, new APPSOSBean(getPackageName(), MainService.class.getName()));
+
+ if (mMainReceiver == null) {
+ // 注册广播接收器
+ mMainReceiver = new MainReceiver(this);
+ mMainReceiver.registerAction(this);
+ }
+
+
+ MainServiceThread.getInstance(this, mMainServiceHandler).start();
+
+ LogUtils.i(TAG, "Main Service Is Start.");
+ }
+ }
+
+ // 唤醒和绑定守护进程
+ //
+ void wakeupAndBindAssistant() {
+ LogUtils.d(TAG, "wakeupAndBindAssistant()");
+// if (ServiceUtils.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);
+// }
+ Intent intent = new Intent(this, AssistantService.class);
+ startService(intent);
+ // 绑定服务的Intent
+ //Intent intent = new Intent(this, AssistantService.class);
+ bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
+
+// Intent intent = new Intent(this, AssistantService.class);
+// startService(intent);
+// LogUtils.d(TAG, "startService(intent)");
+// bindService(new Intent(this, AssistantService.class), mMyServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ @Override
+ public void onDestroy() {
+ //LogUtils.d(TAG, "onDestroy");
+ mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
+ //LogUtils.d(TAG, "onDestroy done");
+ if (mMainServiceBean.isEnable() == false) {
+ // 设置运行状态
+ isServiceRunning = false;// 解除绑定
+ if (isBound) {
+ unbindService(mMyServiceConnection);
+ isBound = false;
+ }
+ // 停止守护进程
+ Intent intent = new Intent(this, AssistantService.class);
+ stopService(intent);
+ // 停止Receiver
+ if (mMainReceiver != null) {
+ unregisterReceiver(mMainReceiver);
+ mMainReceiver = null;
+ }
+ // 停止前台通知栏
+ stopForeground(true);
+
+ // 停止主要进程
+ MainServiceThread.getInstance(this, mMainServiceHandler).setIsExit(true);
+ }
+
+ super.onDestroy();
+ }
+
+ // 主进程与守护进程连接时需要用到此类
+ //
+ private class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LogUtils.d(TAG, "onServiceConnected(...)");
+ AssistantService.MyBinder binder = (AssistantService.MyBinder) service;
+ mAssistantService = binder.getService();
+ isBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ LogUtils.d(TAG, "onServiceDisconnected(...)");
+ if (mMainServiceBean.isEnable()) {
+ // 唤醒守护进程
+ wakeupAndBindAssistant();
+ SOS.sosWinBollService(getApplicationContext(), new APPSOSBean(getPackageName(), MainService.class.getName()));
+ }
+ isBound = false;
+ mAssistantService = null;
+ }
+
+ }
+
+
+ // 用于返回服务实例的Binder
+ public class MyBinder extends Binder {
+ MainService getService() {
+ LogUtils.d(TAG, "MainService MyBinder getService()");
+ return MainService.this;
+ }
+ }
+
+// //
+// // 启动服务
+// //
+// public static void startControlCenterService(Context context) {
+// Intent intent = new Intent(context, MainService.class);
+// context.startForegroundService(intent);
+// }
+//
+// //
+// // 停止服务
+// //
+// public static void stopControlCenterService(Context context) {
+// Intent intent = new Intent(context, MainService.class);
+// context.stopService(intent);
+// }
+
+ public void appenMessage(String message) {
+ LogUtils.d(TAG, String.format("Message : %s", message));
+ }
+
+ public static void stopMainService(Context context) {
+ LogUtils.d(TAG, "stopMainService");
+ MainServiceBean bean = new MainServiceBean();
+ bean.setIsEnable(false);
+ MainServiceBean.saveBean(context, bean);
+ context.stopService(new Intent(context, MainService.class));
+ }
+
+ public static void startMainService(Context context) {
+ LogUtils.d(TAG, "startMainService");
+ MainServiceBean bean = new MainServiceBean();
+ bean.setIsEnable(true);
+ MainServiceBean.saveBean(context, bean);
+ context.startService(new Intent(context, MainService.class));
+ }
+}
+
diff --git a/positions/src/main/java/cc/winboll/studio/positions/threads/MainServiceThread.java b/positions/src/main/java/cc/winboll/studio/positions/threads/MainServiceThread.java
new file mode 100644
index 0000000..c0eab8c
--- /dev/null
+++ b/positions/src/main/java/cc/winboll/studio/positions/threads/MainServiceThread.java
@@ -0,0 +1,77 @@
+package cc.winboll.studio.positions.threads;
+
+/**
+ * @Author ZhanGSKen@AliYun.Com
+ * @Date 2025/02/14 03:46:44
+ */
+import android.content.Context;
+import cc.winboll.studio.positions.handlers.MainServiceHandler;
+import cc.winboll.studio.positions.services.MainService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.SOS;
+import cc.winboll.studio.libappbase.bean.APPSOSBean;
+import com.hjq.toast.ToastUtils;
+import java.lang.ref.WeakReference;
+
+public class MainServiceThread extends Thread {
+
+ public static final String TAG = "MainServiceThread";
+
+ volatile static MainServiceThread _MainServiceThread;
+ // 控制线程是否退出的标志
+ volatile boolean isExit = false;
+ volatile boolean isStarted = false;
+ Context mContext;
+ // 服务Handler, 用于线程发送消息使用
+ WeakReference mwrMainServiceHandler;
+
+ MainServiceThread(Context context, MainServiceHandler handler) {
+ mContext = context;
+ mwrMainServiceHandler = new WeakReference(handler);
+ }
+
+ public void setIsExit(boolean isExit) {
+ this.isExit = isExit;
+ }
+
+ public boolean isExit() {
+ return isExit;
+ }
+
+ public void setIsStarted(boolean isStarted) {
+ this.isStarted = isStarted;
+ }
+
+ public boolean isStarted() {
+ return isStarted;
+ }
+
+ public static MainServiceThread getInstance(Context context, MainServiceHandler handler) {
+ if (_MainServiceThread != null) {
+ _MainServiceThread.setIsExit(true);
+ }
+ _MainServiceThread = new MainServiceThread(context, handler);
+ return _MainServiceThread;
+ }
+
+ @Override
+ public void run() {
+ if (isStarted == false) {
+ isStarted = true;
+ LogUtils.d(TAG, "run()");
+
+ while (!isExit()) {
+ //ToastUtils.show("run");
+ //LogUtils.d(TAG, "run()");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
+ }
+ }
+ _MainServiceThread = null;
+ LogUtils.d(TAG, "run() exit");
+ }
+ }
+
+}
diff --git a/positions/src/main/res/drawable/ic_launcher.xml b/positions/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000..d4d1eaf
--- /dev/null
+++ b/positions/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/positions/src/main/res/drawable/ic_launcher_background.xml b/positions/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..9486190
--- /dev/null
+++ b/positions/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/res/drawable/ic_launcher_disable.xml b/positions/src/main/res/drawable/ic_launcher_disable.xml
new file mode 100644
index 0000000..9a31905
--- /dev/null
+++ b/positions/src/main/res/drawable/ic_launcher_disable.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/positions/src/main/res/drawable/ic_launcher_foreground.xml b/positions/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..872b04e
--- /dev/null
+++ b/positions/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/positions/src/main/res/drawable/ic_launcher_foreground_disable.xml b/positions/src/main/res/drawable/ic_launcher_foreground_disable.xml
new file mode 100644
index 0000000..763b72c
--- /dev/null
+++ b/positions/src/main/res/drawable/ic_launcher_foreground_disable.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/positions/src/main/res/drawable/shape_gradient.xml b/positions/src/main/res/drawable/shape_gradient.xml
new file mode 100644
index 0000000..c164fe9
--- /dev/null
+++ b/positions/src/main/res/drawable/shape_gradient.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/positions/src/main/res/layout/activity_main.xml b/positions/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..5376c86
--- /dev/null
+++ b/positions/src/main/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/res/layout/activity_settings.xml b/positions/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..b77a699
--- /dev/null
+++ b/positions/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/res/layout/fragment_call.xml b/positions/src/main/res/layout/fragment_call.xml
new file mode 100644
index 0000000..1b8673c
--- /dev/null
+++ b/positions/src/main/res/layout/fragment_call.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/res/layout/fragment_contacts.xml b/positions/src/main/res/layout/fragment_contacts.xml
new file mode 100644
index 0000000..aeec420
--- /dev/null
+++ b/positions/src/main/res/layout/fragment_contacts.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/positions/src/main/res/layout/fragment_log.xml b/positions/src/main/res/layout/fragment_log.xml
new file mode 100644
index 0000000..7636182
--- /dev/null
+++ b/positions/src/main/res/layout/fragment_log.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/positions/src/main/res/layout/view_toast.xml b/positions/src/main/res/layout/view_toast.xml
new file mode 100644
index 0000000..d6a9915
--- /dev/null
+++ b/positions/src/main/res/layout/view_toast.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/main/res/menu/toolbar_main.xml b/positions/src/main/res/menu/toolbar_main.xml
new file mode 100644
index 0000000..2aa4890
--- /dev/null
+++ b/positions/src/main/res/menu/toolbar_main.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/positions/src/main/res/values/colors.xml b/positions/src/main/res/values/colors.xml
new file mode 100644
index 0000000..bb20e29
--- /dev/null
+++ b/positions/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+
+ #FF196ABC
+ #FF002B57
+ #FF80BFFF
+
diff --git a/positions/src/main/res/values/strings.xml b/positions/src/main/res/values/strings.xml
new file mode 100644
index 0000000..3e93516
--- /dev/null
+++ b/positions/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ Positions
+
+
diff --git a/positions/src/main/res/values/styles.xml b/positions/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8380acf
--- /dev/null
+++ b/positions/src/main/res/values/styles.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/positions/src/main/res/xml/studio_provider.xml b/positions/src/main/res/xml/studio_provider.xml
new file mode 100644
index 0000000..f045677
--- /dev/null
+++ b/positions/src/main/res/xml/studio_provider.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/stage/AndroidManifest.xml b/positions/src/stage/AndroidManifest.xml
new file mode 100644
index 0000000..ee78d9f
--- /dev/null
+++ b/positions/src/stage/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/positions/src/stage/res/values/strings.xml b/positions/src/stage/res/values/strings.xml
new file mode 100644
index 0000000..ace0c41
--- /dev/null
+++ b/positions/src/stage/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/settings.gradle-demo b/settings.gradle-demo
index 860f719..cfef30c 100644
--- a/settings.gradle-demo
+++ b/settings.gradle-demo
@@ -37,3 +37,8 @@
//include ':aes'
//include ':libaes'
//rootProject.name = "aes"
+
+// Positions 项目编译设置
+//include ':positions'
+//rootProject.name = "positions"
+
diff --git a/winboll-shared/build.properties b/winboll-shared/build.properties
index d4e3b95..43e8ff3 100644
--- a/winboll-shared/build.properties
+++ b/winboll-shared/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Fri Nov 29 11:55:20 GMT 2024
-stageCount=0
+#Fri Feb 21 17:45:37 GMT 2025
+stageCount=1
libraryProject=winboll-shared
-baseVersion=1.8
-publishVersion=1.8.16
-buildCount=0
-baseBetaVersion=1.8.17
+baseVersion=1.0
+publishVersion=1.0.0
+buildCount=1
+baseBetaVersion=1.0.1