Merge branch 'appbase' into apputils
This commit is contained in:
		
							
								
								
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					# Default ignored files
 | 
				
			||||||
 | 
					/shelf/
 | 
				
			||||||
 | 
					/workspace.xml
 | 
				
			||||||
							
								
								
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.idea/.name
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					appbase
 | 
				
			||||||
							
								
								
									
										6
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/compiler.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="CompilerConfiguration">
 | 
				
			||||||
 | 
					    <bytecodeTargetLevel target="17" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										10
									
								
								.idea/deploymentTargetDropDown.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/deploymentTargetDropDown.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="deploymentTargetDropDown">
 | 
				
			||||||
 | 
					    <value>
 | 
				
			||||||
 | 
					      <entry key="appbase">
 | 
				
			||||||
 | 
					        <State />
 | 
				
			||||||
 | 
					      </entry>
 | 
				
			||||||
 | 
					    </value>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										10
									
								
								.idea/migrations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/migrations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="ProjectMigrations">
 | 
				
			||||||
 | 
					    <option name="MigrateToGradleLocalJavaHome">
 | 
				
			||||||
 | 
					      <set>
 | 
				
			||||||
 | 
					        <option value="$PROJECT_DIR$" />
 | 
				
			||||||
 | 
					      </set>
 | 
				
			||||||
 | 
					    </option>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										10
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="ExternalStorageConfigurationManager" enabled="true" />
 | 
				
			||||||
 | 
					  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
 | 
				
			||||||
 | 
					    <output url="file://$PROJECT_DIR$/build/classes" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					  <component name="ProjectType">
 | 
				
			||||||
 | 
					    <option name="id" value="Android" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
@@ -18,18 +18,18 @@ def genVersionName(def versionName){
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android {
 | 
					android {
 | 
				
			||||||
    compileSdkVersion 32
 | 
					    compileSdkVersion 30
 | 
				
			||||||
    buildToolsVersion "33.0.3"
 | 
					    buildToolsVersion "30.0.3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    defaultConfig {
 | 
					    defaultConfig {
 | 
				
			||||||
        applicationId "cc.winboll.studio.appbase"
 | 
					        applicationId "cc.winboll.studio.appbase"
 | 
				
			||||||
        minSdkVersion 21
 | 
					        minSdkVersion 26
 | 
				
			||||||
        targetSdkVersion 30
 | 
					        targetSdkVersion 29
 | 
				
			||||||
        versionCode 1
 | 
					        versionCode 1
 | 
				
			||||||
        // versionName 更新后需要手动设置 
 | 
					        // versionName 更新后需要手动设置 
 | 
				
			||||||
        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
					        // .winboll/winbollBuildProps.properties 文件的 stageCount=0
 | 
				
			||||||
        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
					        // Gradle编译环境下合起来的 versionName 就是 "${versionName}.0"
 | 
				
			||||||
        versionName "1.0" 
 | 
					        versionName "2.1"
 | 
				
			||||||
        if(true) {
 | 
					        if(true) {
 | 
				
			||||||
            versionName = genVersionName("${versionName}")
 | 
					            versionName = genVersionName("${versionName}")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -41,14 +41,30 @@ android {
 | 
				
			|||||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
					            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    compileOptions {
 | 
					 | 
				
			||||||
        sourceCompatibility JavaVersion.VERSION_11
 | 
					 | 
				
			||||||
        targetCompatibility JavaVersion.VERSION_11
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    api project(':libappbase')
 | 
					    api project(':libappbase')
 | 
				
			||||||
	api fileTree(dir: 'libs', include: ['*.jar'])
 | 
					    api fileTree(dir: 'libs', include: ['*.jar'])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // SSH
 | 
				
			||||||
 | 
					    implementation 'com.jcraft:jsch:0.1.55'
 | 
				
			||||||
 | 
					    // Html 解析
 | 
				
			||||||
 | 
					    implementation 'org.jsoup:jsoup:1.13.1'
 | 
				
			||||||
 | 
					    // 二维码类库
 | 
				
			||||||
 | 
					    implementation 'com.google.zxing:core:3.4.1'
 | 
				
			||||||
 | 
					    implementation 'com.journeyapps:zxing-android-embedded:3.6.0'
 | 
				
			||||||
 | 
					    // 应用介绍页类库
 | 
				
			||||||
 | 
					    implementation 'io.github.medyo:android-about-page:2.0.0'
 | 
				
			||||||
 | 
					    // 吐司类库
 | 
				
			||||||
 | 
					    implementation 'com.github.getActivity:ToastUtils:10.5'
 | 
				
			||||||
 | 
					    // 网络连接类库
 | 
				
			||||||
 | 
					    implementation 'com.squareup.okhttp3:okhttp:4.4.1'
 | 
				
			||||||
 | 
					    // Android 类库
 | 
				
			||||||
 | 
					    implementation 'androidx.appcompat:appcompat:1.1.0'
 | 
				
			||||||
 | 
					    implementation 'androidx.viewpager:viewpager:1.0.0'
 | 
				
			||||||
 | 
					    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
 | 
				
			||||||
 | 
					    implementation 'androidx.vectordrawable:vectordrawable-animated:1.1.0'
 | 
				
			||||||
 | 
					    implementation 'androidx.fragment:fragment:1.1.0'
 | 
				
			||||||
 | 
					    implementation 'com.google.android.material:material:1.4.0'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
#Created by .winboll/winboll_app_build.gradle
 | 
					#Created by .winboll/winboll_app_build.gradle
 | 
				
			||||||
#Sun Jan 05 02:30:11 GMT 2025
 | 
					#Sun Mar 09 09:15:39 HKT 2025
 | 
				
			||||||
stageCount=0
 | 
					stageCount=6
 | 
				
			||||||
libraryProject=
 | 
					libraryProject=libappbase
 | 
				
			||||||
baseVersion=1.0
 | 
					baseVersion=2.1
 | 
				
			||||||
publishVersion=1.0.0
 | 
					publishVersion=2.1.5
 | 
				
			||||||
buildCount=2
 | 
					buildCount=0
 | 
				
			||||||
baseBetaVersion=1.0.1
 | 
					baseBetaVersion=2.1.6
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,15 +21,87 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                <category android:name="android.intent.category.LAUNCHER"/>
 | 
					                <category android:name="android.intent.category.LAUNCHER"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.DEFAULT"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </activity>
 | 
					        </activity>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity android:name=".GlobalApplication$CrashActivity"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name=".MyTileService"
 | 
				
			||||||
 | 
					            android:label="@string/tileservice_name"
 | 
				
			||||||
 | 
					            android:icon="@drawable/ic_launcher"
 | 
				
			||||||
 | 
					            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.service.quicksettings.action.QS_TILE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </service>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name=".services.MainService"
 | 
				
			||||||
 | 
					            android:exported="true"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service android:name=".services.AssistantService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver android:name="cc.winboll.studio.appbase.receivers.MainReceiver">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.appbase.receivers.MainReceiver"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver
 | 
				
			||||||
 | 
					            android:name=".widgets.APPNewsWidget"
 | 
				
			||||||
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_WAKEUP_SERVICE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidget.ACTION_RELOAD_REPORT"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <meta-data
 | 
				
			||||||
 | 
					                android:name="android.appwidget.provider"
 | 
				
			||||||
 | 
					                android:resource="@xml/widget_provider_info_sos"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver android:name=".widgets.APPNewsWidgetClickListener">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidgetClickListener.ACTION_PRE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.appbase.widgets.APPNewsWidgetClickListener.ACTION_NEXT"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <meta-data
 | 
					        <meta-data
 | 
				
			||||||
            android:name="android.max_aspect"
 | 
					            android:name="android.max_aspect"
 | 
				
			||||||
            android:value="4.0"/>
 | 
					            android:value="4.0"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity android:name=".GlobalApplication$CrashActivity"/>
 | 
					        <service android:name="cc.winboll.studio.appbase.services.TestDemoBindService"
 | 
				
			||||||
 | 
					            android:exported="true"/>
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        <service android:name="cc.winboll.studio.appbase.services.TestDemoService"
 | 
				
			||||||
 | 
					            android:exported="true"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,12 +5,24 @@ package cc.winboll.studio.appbase;
 | 
				
			|||||||
 * @Date 2025/01/05 09:54:42
 | 
					 * @Date 2025/01/05 09:54:42
 | 
				
			||||||
 * @Describe APPbase 应用类
 | 
					 * @Describe APPbase 应用类
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 import cc.winboll.studio.GlobalApplication;
 | 
					import cc.winboll.studio.libappbase.GlobalApplication;
 | 
				
			||||||
 | 
					import android.content.IntentFilter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOSCenterServiceReceiver;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class App extends GlobalApplication {
 | 
					public class App extends GlobalApplication {
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    public static final String TAG = "App";
 | 
					    public static final String TAG = "App";
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    SOSCenterServiceReceiver mSOSCenterServiceReceiver;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					        GlobalApplication.setIsDebuging(this, BuildConfig.DEBUG);
 | 
				
			||||||
 | 
					        mSOSCenterServiceReceiver = new SOSCenterServiceReceiver();
 | 
				
			||||||
 | 
					        IntentFilter intentFilter = new IntentFilter();
 | 
				
			||||||
 | 
					        intentFilter.addAction(SOS.ACTION_SOS);
 | 
				
			||||||
 | 
					        registerReceiver(mSOSCenterServiceReceiver, intentFilter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,141 @@
 | 
				
			|||||||
package cc.winboll.studio.appbase;
 | 
					package cc.winboll.studio.appbase;
 | 
				
			||||||
 
 | 
					
 | 
				
			||||||
import android.app.Activity;
 | 
					import android.content.Intent;
 | 
				
			||||||
import android.os.Bundle;
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
public class MainActivity extends Activity {
 | 
					import android.widget.CheckBox;
 | 
				
			||||||
     
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
    @Override
 | 
					import androidx.appcompat.widget.Toolbar;
 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					import cc.winboll.studio.appbase.R;
 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					import cc.winboll.studio.appbase.services.MainService;
 | 
				
			||||||
        setContentView(R.layout.activity_main);
 | 
					import cc.winboll.studio.appbase.services.TestDemoBindService;
 | 
				
			||||||
        
 | 
					import cc.winboll.studio.appbase.services.TestDemoService;
 | 
				
			||||||
    }
 | 
					import cc.winboll.studio.libappbase.GlobalApplication;
 | 
				
			||||||
	
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
}
 | 
					import cc.winboll.studio.libappbase.LogView;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.widgets.StatusWidget;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					import android.content.ComponentName;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOS;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOSObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MainActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "MainActivity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LogView mLogView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        ToastUtils.show("onCreate");
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_main);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Toolbar toolbar = findViewById(R.id.activitymainToolbar1);
 | 
				
			||||||
 | 
					        setSupportActionBar(toolbar);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CheckBox cbIsDebugMode = findViewById(R.id.activitymainCheckBox1);
 | 
				
			||||||
 | 
					        cbIsDebugMode.setChecked(GlobalApplication.isDebuging());
 | 
				
			||||||
 | 
					        mLogView = findViewById(R.id.activitymainLogView1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (GlobalApplication.isDebuging()) { mLogView.start(); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					        Intent intentAPPWidget = new Intent(this, StatusWidget.class);
 | 
				
			||||||
 | 
					        intentAPPWidget.setAction(StatusWidget.ACTION_STATUS_UPDATE);
 | 
				
			||||||
 | 
					        sendBroadcast(intentAPPWidget);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onResume() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onResume");
 | 
				
			||||||
 | 
					        super.onResume();
 | 
				
			||||||
 | 
					        mLogView.start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public void onSwitchDebugMode(View view) {
 | 
				
			||||||
 | 
					        GlobalApplication.setIsDebuging(this, ((CheckBox)view).isChecked());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStartCenter(View view) {
 | 
				
			||||||
 | 
					        MainService.startMainService(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStopCenter(View view) {
 | 
				
			||||||
 | 
					        MainService.stopMainService(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onTestStopMainServiceWithoutSettingEnable(View view) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onTestStopMainServiceWithoutSettingEnable");
 | 
				
			||||||
 | 
					        stopService(new Intent(this, MainService.class));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onTestUseComponentStartService(View view) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onTestUseComponentStartService");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 目标服务的包名和类名
 | 
				
			||||||
 | 
					        String packageName = this.getPackageName();
 | 
				
			||||||
 | 
					        String serviceClassName = TestDemoService.class.getName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 构建Intent
 | 
				
			||||||
 | 
					        Intent intentService = new Intent();
 | 
				
			||||||
 | 
					        intentService.setComponent(new ComponentName(packageName, serviceClassName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        startService(intentService);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onTestSOS(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoService.class);
 | 
				
			||||||
 | 
					        stopService(intent);
 | 
				
			||||||
 | 
					        if(App.isDebuging()) {
 | 
				
			||||||
 | 
					            SOS.sosToAppBaseBeta(this, TestDemoService.class.getName());
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            SOS.sosToAppBase(this, TestDemoService.class.getName());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onSartTestDemoService(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoService.class);
 | 
				
			||||||
 | 
					        intent.setAction(TestDemoService.ACTION_ENABLE);
 | 
				
			||||||
 | 
					        startService(intent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStopTestDemoService(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoService.class);
 | 
				
			||||||
 | 
					        intent.setAction(TestDemoService.ACTION_DISABLE);
 | 
				
			||||||
 | 
					        startService(intent);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Intent intentStop = new Intent(this, TestDemoService.class);
 | 
				
			||||||
 | 
					        stopService(intentStop);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStopTestDemoServiceNoSettings(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoService.class);
 | 
				
			||||||
 | 
					        stopService(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void onSartTestDemoBindService(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoBindService.class);
 | 
				
			||||||
 | 
					        intent.setAction(TestDemoBindService.ACTION_ENABLE);
 | 
				
			||||||
 | 
					        startService(intent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStopTestDemoBindService(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoBindService.class);
 | 
				
			||||||
 | 
					        intent.setAction(TestDemoBindService.ACTION_DISABLE);
 | 
				
			||||||
 | 
					        startService(intent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Intent intentStop = new Intent(this, TestDemoBindService.class);
 | 
				
			||||||
 | 
					        stopService(intentStop);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void onStopTestDemoBindServiceNoSettings(View view) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, TestDemoBindService.class);
 | 
				
			||||||
 | 
					        stopService(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/13 19:30:10
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.service.quicksettings.Tile;
 | 
				
			||||||
 | 
					import android.service.quicksettings.TileService;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.MainServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.services.MainService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MyTileService extends TileService {
 | 
				
			||||||
 | 
					    public static final String TAG = "MyTileService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static MyTileService _MyTileService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onStartListening() {
 | 
				
			||||||
 | 
					        super.onStartListening();
 | 
				
			||||||
 | 
					        _MyTileService = this;
 | 
				
			||||||
 | 
					        Tile tile = getQsTile();
 | 
				
			||||||
 | 
					        MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean != null && bean.isEnable()) {
 | 
				
			||||||
 | 
					            //MainService.startMainService(context);
 | 
				
			||||||
 | 
					            tile.setState(Tile.STATE_ACTIVE);
 | 
				
			||||||
 | 
					            tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            //MainService.stopMainService(context);
 | 
				
			||||||
 | 
					            tile.setState(Tile.STATE_INACTIVE);
 | 
				
			||||||
 | 
					            tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        tile.updateTile();
 | 
				
			||||||
 | 
					//        Tile tile = getQsTile();
 | 
				
			||||||
 | 
					//        tile.setState(Tile.STATE_INACTIVE);
 | 
				
			||||||
 | 
					//        tile.setLabel(getString(R.string.tileservice_name));
 | 
				
			||||||
 | 
					//        tile.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.drawable.ic_cloud_outline));
 | 
				
			||||||
 | 
					//        tile.updateTile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onClick() {
 | 
				
			||||||
 | 
					        super.onClick();
 | 
				
			||||||
 | 
					        Tile tile = getQsTile();
 | 
				
			||||||
 | 
					        MainServiceBean bean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new MainServiceBean();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (tile.getState() == Tile.STATE_ACTIVE) {
 | 
				
			||||||
 | 
					            bean.setIsEnable(false);
 | 
				
			||||||
 | 
					            MainServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            MainService.stopMainService(this);
 | 
				
			||||||
 | 
					        } else if (tile.getState() == Tile.STATE_INACTIVE) {
 | 
				
			||||||
 | 
					            bean.setIsEnable(true);
 | 
				
			||||||
 | 
					            MainServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            MainService.startMainService(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        updateServiceIconStatus(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void updateServiceIconStatus(Context context) {
 | 
				
			||||||
 | 
					        if (_MyTileService == null) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Tile tile = _MyTileService.getQsTile();
 | 
				
			||||||
 | 
					        MainServiceBean bean = MainServiceBean.loadBean(context, MainServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean != null && bean.isEnable()) {
 | 
				
			||||||
 | 
					            tile.setState(Tile.STATE_ACTIVE);
 | 
				
			||||||
 | 
					            tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            tile.setState(Tile.STATE_INACTIVE);
 | 
				
			||||||
 | 
					            tile.setIcon(android.graphics.drawable.Icon.createWithResource(context, R.drawable.ic_cloud_outline));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        tile.updateTile();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.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);
 | 
				
			||||||
 | 
					        jsonWriter.name("isEnable").value(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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.beans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/03/07 12:47:22
 | 
				
			||||||
 | 
					 * @Describe TestServiceBean
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.util.JsonReader;
 | 
				
			||||||
 | 
					import android.util.JsonWriter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.BaseBean;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TestDemoBindServiceBean extends BaseBean {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "TestServiceBean";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean isEnable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public TestDemoBindServiceBean() {
 | 
				
			||||||
 | 
					        this.isEnable = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsEnable(boolean isEnable) {
 | 
				
			||||||
 | 
					        this.isEnable = isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isEnable() {
 | 
				
			||||||
 | 
					        return isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return TestDemoBindServiceBean.class.getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
				
			||||||
 | 
					        super.writeThisToJsonWriter(jsonWriter);
 | 
				
			||||||
 | 
					        jsonWriter.name("isEnable").value(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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.beans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/03/07 12:49:21
 | 
				
			||||||
 | 
					 * @Describe TestDemoServiceBean
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.util.JsonReader;
 | 
				
			||||||
 | 
					import android.util.JsonWriter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.BaseBean;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TestDemoServiceBean extends BaseBean {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "TestDemoServiceBean";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean isEnable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public TestDemoServiceBean() {
 | 
				
			||||||
 | 
					        this.isEnable = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsEnable(boolean isEnable) {
 | 
				
			||||||
 | 
					        this.isEnable = isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isEnable() {
 | 
				
			||||||
 | 
					        return isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return TestDemoServiceBean.class.getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
				
			||||||
 | 
					        super.writeThisToJsonWriter(jsonWriter);
 | 
				
			||||||
 | 
					        jsonWriter.name("isEnable").value(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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.beans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/17 10:05:09
 | 
				
			||||||
 | 
					 * @Describe APPSOSReportBean
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					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";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    protected String message;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public WinBollNewsBean() {
 | 
				
			||||||
 | 
					        this.message = "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public WinBollNewsBean(String message) {
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setMessage(String message) {
 | 
				
			||||||
 | 
					        this.message = message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return message;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return WinBollNewsBean.class.getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
				
			||||||
 | 
					        super.writeThisToJsonWriter(jsonWriter);
 | 
				
			||||||
 | 
					        jsonWriter.name("message").value(getMessage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
				
			||||||
 | 
					        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
				
			||||||
 | 
					            if (name.equals("message")) {
 | 
				
			||||||
 | 
					                setMessage(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,38 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.handlers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/14 03:51:40
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Message;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.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<MainService> serviceWeakReference;
 | 
				
			||||||
 | 
					    public MainServiceHandler(MainService service) {
 | 
				
			||||||
 | 
					        serviceWeakReference = new WeakReference<MainService>(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;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.receivers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/13 06:58:04
 | 
				
			||||||
 | 
					 * @Describe 主要广播接收器
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.appwidget.AppWidgetManager;
 | 
				
			||||||
 | 
					import android.content.BroadcastReceiver;
 | 
				
			||||||
 | 
					import android.content.ComponentName;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.IntentFilter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.WinBollNewsBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.services.MainService;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.widgets.APPNewsWidget;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.AppUtils;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.lang.ref.WeakReference;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.WinBoll;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.APPModel;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOS;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOSObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MainReceiver extends BroadcastReceiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "MainReceiver";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    WeakReference<MainService> mwrService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainReceiver(MainService service) {
 | 
				
			||||||
 | 
					        mwrService = new WeakReference<MainService>(service);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					        String szAction = intent.getAction();
 | 
				
			||||||
 | 
					        if (szAction.equals(ACTION_BOOT_COMPLETED)) {
 | 
				
			||||||
 | 
					            ToastUtils.show("ACTION_BOOT_COMPLETED");
 | 
				
			||||||
 | 
					        } else if (szAction.equals(WinBoll.ACTION_BIND)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_BIND");
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("context.getPackageName() %s", context.getPackageName()));
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("intent.getAction() %s", intent.getAction()));
 | 
				
			||||||
 | 
					            String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
 | 
				
			||||||
 | 
					            if (szAPPModel != null && !szAPPModel.equals("")) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
 | 
				
			||||||
 | 
					                    if (bean != null) {
 | 
				
			||||||
 | 
					                        String szAppPackageName = bean.getAppPackageName();
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
 | 
				
			||||||
 | 
					                        String szAppMainServiveName = bean.getAppMainServiveName();
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
 | 
				
			||||||
 | 
					                        mwrService.get().bindAPPModelConnection(bean);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (intent.getAction().equals(SOS.ACTION_SOS)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_SOS");
 | 
				
			||||||
 | 
					            String sos = intent.getStringExtra(SOS.EXTRA_OBJECT);
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("SOS %s", sos));
 | 
				
			||||||
 | 
					            if (sos != null && !sos.equals("")) {
 | 
				
			||||||
 | 
					                SOSObject bean = SOS.parseSOSObject(sos);
 | 
				
			||||||
 | 
					                if (bean != null) {
 | 
				
			||||||
 | 
					                    String szObjectPackageName = bean.getObjectPackageName();
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, String.format("szObjectPackageName %s", szObjectPackageName));
 | 
				
			||||||
 | 
					                    String szObjectServiveName = bean.getObjectServiveName();
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, String.format("szObjectServiveName %s", szObjectServiveName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Intent intentService = new Intent();
 | 
				
			||||||
 | 
					                    intentService.setComponent(new ComponentName(szObjectPackageName, szObjectServiveName));
 | 
				
			||||||
 | 
					                    context.startService(intentService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    String appName = AppUtils.getAppNameByPackageName(context, szObjectPackageName);
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, String.format("appName %s", appName));
 | 
				
			||||||
 | 
					                    WinBollNewsBean appWinBollNewsBean = new WinBollNewsBean(appName);
 | 
				
			||||||
 | 
					                    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
 | 
				
			||||||
 | 
					                    String currentTime = sdf.format(new Date());
 | 
				
			||||||
 | 
					                    StringBuilder sbLine = new StringBuilder();
 | 
				
			||||||
 | 
					                    sbLine.append("[");
 | 
				
			||||||
 | 
					                    sbLine.append(currentTime);
 | 
				
			||||||
 | 
					                    sbLine.append("] Power to ");
 | 
				
			||||||
 | 
					                    sbLine.append(appName);
 | 
				
			||||||
 | 
					                    appWinBollNewsBean.setMessage(sbLine.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    APPNewsWidget.addWinBollNewsBean(context, appWinBollNewsBean);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Intent intentWidget = new Intent(context, APPNewsWidget.class);
 | 
				
			||||||
 | 
					                    intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
 | 
				
			||||||
 | 
					                    context.sendBroadcast(intentWidget);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ToastUtils.show(szAction);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 注册 Receiver
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    public void registerAction(MainService service) {
 | 
				
			||||||
 | 
					        IntentFilter filter=new IntentFilter();
 | 
				
			||||||
 | 
					        filter.addAction(ACTION_BOOT_COMPLETED);
 | 
				
			||||||
 | 
					        filter.addAction(SOS.ACTION_SOS);
 | 
				
			||||||
 | 
					        filter.addAction(WinBoll.ACTION_BIND);
 | 
				
			||||||
 | 
					        //filter.addAction(Intent.ACTION_BATTERY_CHANGED);
 | 
				
			||||||
 | 
					        service.registerReceiver(this, filter);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.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.IBinder;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,316 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.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.appbase.MyTileService;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.MainServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.handlers.MainServiceHandler;
 | 
				
			||||||
 | 
					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.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.APPModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					    ArrayList<APPConnection> mAPPModelConnectionList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public IBinder onBind(Intent intent) {
 | 
				
			||||||
 | 
					        return new MyBinder();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainServiceThread getRemindThread() {
 | 
				
			||||||
 | 
					        return mMainServiceThread;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onCreate()");
 | 
				
			||||||
 | 
					        mAPPModelConnectionList = new ArrayList<APPConnection>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (mMainReceiver == null) {
 | 
				
			||||||
 | 
					                // 注册广播接收器
 | 
				
			||||||
 | 
					                mMainReceiver = new MainReceiver(this);
 | 
				
			||||||
 | 
					                mMainReceiver.registerAction(this);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 启动小部件
 | 
				
			||||||
 | 
					            Intent intentTimeWidget = new Intent(this, APPNewsWidget.class);
 | 
				
			||||||
 | 
					            intentTimeWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
 | 
				
			||||||
 | 
					            this.sendBroadcast(intentTimeWidget);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            startMainServiceThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MyTileService.updateServiceIconStatus(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            LogUtils.i(TAG, "Main Service Is Start.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 唤醒和绑定守护进程
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    void wakeupAndBindAssistant() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "wakeupAndBindAssistant()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, AssistantService.class);
 | 
				
			||||||
 | 
					        startService(intent);
 | 
				
			||||||
 | 
					        // 绑定服务的Intent
 | 
				
			||||||
 | 
					        bindService(intent, mMyServiceConnection, Context.BIND_IMPORTANT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 开启提醒铃声线程
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    public void startMainServiceThread() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "startMainServiceThread");
 | 
				
			||||||
 | 
					        if (mMainServiceThread == null) {
 | 
				
			||||||
 | 
					            mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "new MainServiceThread");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (mMainServiceThread.isExist() == true) {
 | 
				
			||||||
 | 
					                mMainServiceThread = new MainServiceThread(this, mMainServiceHandler);
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "renew MainServiceThread");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // 提醒进程正在进行中就更新状态后退出
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "A mMainServiceThread running.");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        mMainServiceThread.start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void stopRemindThread() {
 | 
				
			||||||
 | 
					        if (mMainServiceThread != null) {
 | 
				
			||||||
 | 
					            mMainServiceThread.setIsExist(true);
 | 
				
			||||||
 | 
					            mMainServiceThread = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDestroy() {
 | 
				
			||||||
 | 
					        //LogUtils.d(TAG, "onDestroy");
 | 
				
			||||||
 | 
					        mMainServiceBean = MainServiceBean.loadBean(this, MainServiceBean.class);
 | 
				
			||||||
 | 
					        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);
 | 
				
			||||||
 | 
					            // 停止消息提醒进程
 | 
				
			||||||
 | 
					            stopRemindThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MyTileService.updateServiceIconStatus(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            super.onDestroy();
 | 
				
			||||||
 | 
					            //LogUtils.d(TAG, "onDestroy done");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void bindAPPModelConnection(APPModel bean) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "bindAPPModelConnection(...)");
 | 
				
			||||||
 | 
					        // 清理旧的绑定链接
 | 
				
			||||||
 | 
					        for (int i = mAPPModelConnectionList.size() - 1; i > -1; i--) {
 | 
				
			||||||
 | 
					            APPConnection item = mAPPModelConnectionList.get(i);
 | 
				
			||||||
 | 
					            if (item.isBindToAPP(bean)) {
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "Bind Servive exist.");
 | 
				
			||||||
 | 
					                unbindService(item);
 | 
				
			||||||
 | 
					                mAPPModelConnectionList.remove(i);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 绑定服务
 | 
				
			||||||
 | 
					        APPConnection appConnection = new APPConnection();
 | 
				
			||||||
 | 
					        Intent intentService = new Intent();
 | 
				
			||||||
 | 
					        intentService.setComponent(new ComponentName(bean.getAppPackageName(), bean.getAppMainServiveName()));
 | 
				
			||||||
 | 
					        bindService(intentService, appConnection, Context.BIND_IMPORTANT);
 | 
				
			||||||
 | 
					        mAPPModelConnectionList.add(appConnection);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Intent intentWidget = new Intent(this, APPNewsWidget.class);
 | 
				
			||||||
 | 
					        intentWidget.setAction(APPNewsWidget.ACTION_WAKEUP_SERVICE);
 | 
				
			||||||
 | 
					        APPModel appSOSBean = new APPModel(bean.getAppPackageName(), bean.getAppMainServiveName());
 | 
				
			||||||
 | 
					        intentWidget.putExtra("APPSOSBean", appSOSBean.toString());
 | 
				
			||||||
 | 
					        sendBroadcast(intentWidget);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class APPConnection implements ServiceConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ComponentName mComponentName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        boolean isBindToAPP(APPModel bean) {
 | 
				
			||||||
 | 
					            return mComponentName != null
 | 
				
			||||||
 | 
					                && mComponentName.getClassName().equals(bean.getAppMainServiveName())
 | 
				
			||||||
 | 
					                && mComponentName.getPackageName().equals(bean.getAppPackageName());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onServiceConnected(ComponentName name, IBinder service) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "onServiceConnected(...)");
 | 
				
			||||||
 | 
					            mComponentName = name;
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("onServiceConnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onServiceDisconnected(ComponentName name) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "onServiceDisconnected(...)");
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("onServiceDisconnected : \ngetClassName %s\ngetPackageName %s", name.getClassName(), name.getPackageName()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 尝试无参数启动一下服务
 | 
				
			||||||
 | 
					            String appPackage = mComponentName.getPackageName();
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("appPackage %s", appPackage));
 | 
				
			||||||
 | 
					            String appMainServiceClassName = mComponentName.getClassName();
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("appMainServiceClassName %s", appMainServiceClassName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Intent intentService = new Intent();
 | 
				
			||||||
 | 
					            intentService.setComponent(new ComponentName(appPackage, appMainServiceClassName));
 | 
				
			||||||
 | 
					            startService(intentService);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 主进程与守护进程连接时需要用到此类
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    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();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/03/07 12:45:49
 | 
				
			||||||
 | 
					 * @Describe 启动时申请绑定到APPBase主服务的服务示例
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.Service;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.os.Binder;
 | 
				
			||||||
 | 
					import android.os.IBinder;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.TestDemoBindServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.WinBoll;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.App;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.SOS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TestDemoBindService extends Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "TestDemoBindService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String ACTION_ENABLE = TestDemoBindService.class.getName() + ".ACTION_ENABLE";
 | 
				
			||||||
 | 
					    public static final String ACTION_DISABLE = TestDemoBindService.class.getName() + ".ACTION_DISABLE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static TestThread _TestThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static boolean _IsRunning;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized static void setIsRunning(boolean isRunning) {
 | 
				
			||||||
 | 
					        _IsRunning = isRunning;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static boolean isRunning() {
 | 
				
			||||||
 | 
					        return _IsRunning;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public IBinder onBind(Intent intent) {
 | 
				
			||||||
 | 
					        return new MyBinder();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class MyBinder extends Binder {
 | 
				
			||||||
 | 
					        public TestDemoBindService getService() {
 | 
				
			||||||
 | 
					            return TestDemoBindService.this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onCreate()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int onStartCommand(Intent intent, int flags, int startId) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onStartCommand(...)");
 | 
				
			||||||
 | 
					        TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new TestDemoBindServiceBean();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (intent.getAction() != null) {
 | 
				
			||||||
 | 
					            if (intent.getAction().equals(ACTION_ENABLE)) {
 | 
				
			||||||
 | 
					                bean.setIsEnable(true);
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "setIsEnable(true);");
 | 
				
			||||||
 | 
					                TestDemoBindServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            } else if (intent.getAction().equals(ACTION_DISABLE)) {
 | 
				
			||||||
 | 
					                bean.setIsEnable(false);
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "setIsEnable(false);");
 | 
				
			||||||
 | 
					                TestDemoBindServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
 | 
				
			||||||
 | 
					        //return super.onStartCommand(intent, flags, startId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void run() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					        TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new TestDemoBindServiceBean();
 | 
				
			||||||
 | 
					            TestDemoBindServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (bean.isEnable()) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "run() bean.isEnable()");
 | 
				
			||||||
 | 
					            TestThread.getInstance(this).start();
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "_TestThread.start()");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onDestroy()");
 | 
				
			||||||
 | 
					        TestDemoBindServiceBean bean = TestDemoBindServiceBean.loadBean(this, TestDemoBindServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new TestDemoBindServiceBean();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TestThread.getInstance(this).setIsExit(true);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 预防 APPBase 应用重启绑定失效。
 | 
				
			||||||
 | 
					        // 所以退出时检查本服务是否配置启用,如果启用就发送一个 SOS 信号。
 | 
				
			||||||
 | 
					        // 这样 APPBase 就会用组件方式启动本服务。
 | 
				
			||||||
 | 
					        if (bean.isEnable()) {
 | 
				
			||||||
 | 
					            if (App.isDebuging()) {
 | 
				
			||||||
 | 
					                SOS.sosToAppBaseBeta(this, TestDemoBindService.class.getName());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                SOS.sosToAppBase(this, TestDemoBindService.class.getName());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _IsRunning = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class TestThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        volatile static TestThread _TestThread;
 | 
				
			||||||
 | 
					        Context mContext;
 | 
				
			||||||
 | 
					        volatile boolean isStarted = false;
 | 
				
			||||||
 | 
					        volatile boolean isExit = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TestThread(Context context) {
 | 
				
			||||||
 | 
					            super();
 | 
				
			||||||
 | 
					            mContext = context;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static synchronized TestThread getInstance(Context context) {
 | 
				
			||||||
 | 
					            if (_TestThread != null) {
 | 
				
			||||||
 | 
					                _TestThread.setIsExit(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _TestThread = new TestThread(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return _TestThread;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public synchronized void setIsExit(boolean isExit) {
 | 
				
			||||||
 | 
					            this.isExit = isExit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isExit() {
 | 
				
			||||||
 | 
					            return isExit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            if (isStarted == false) {
 | 
				
			||||||
 | 
					                isStarted = true;
 | 
				
			||||||
 | 
					                super.run();
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "run() start");
 | 
				
			||||||
 | 
					                if (App.isDebuging()) {
 | 
				
			||||||
 | 
					                    WinBoll.bindToAPPBaseBeta(mContext, TestDemoBindService.class.getName());
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    WinBoll.bindToAPPBase(mContext, TestDemoBindService.class.getName());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!isExit()) {
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        Thread.sleep(1000);
 | 
				
			||||||
 | 
					                    } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "run() exit");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.services;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/03/07 12:39:24
 | 
				
			||||||
 | 
					 * @Describe 普通服务示例
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.Service;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.os.Binder;
 | 
				
			||||||
 | 
					import android.os.IBinder;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.beans.TestDemoServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.WinBoll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TestDemoService extends Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "TestDemoService";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String ACTION_ENABLE = TestDemoService.class.getName() + ".ACTION_ENABLE";
 | 
				
			||||||
 | 
					    public static final String ACTION_DISABLE = TestDemoService.class.getName() + ".ACTION_DISABLE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static TestThread _TestThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static boolean _IsRunning;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized static void setIsRunning(boolean isRunning) {
 | 
				
			||||||
 | 
					        _IsRunning = isRunning;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static boolean isRunning() {
 | 
				
			||||||
 | 
					        return _IsRunning;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public IBinder onBind(Intent intent) {
 | 
				
			||||||
 | 
					        return new MyBinder();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class MyBinder extends Binder {
 | 
				
			||||||
 | 
					        public TestDemoService getService() {
 | 
				
			||||||
 | 
					            return TestDemoService.this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onCreate()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int onStartCommand(Intent intent, int flags, int startId) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onStartCommand(...)");
 | 
				
			||||||
 | 
					        TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new TestDemoServiceBean();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (intent.getAction() != null) {
 | 
				
			||||||
 | 
					            if (intent.getAction().equals(ACTION_ENABLE)) {
 | 
				
			||||||
 | 
					                bean.setIsEnable(true);
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "setIsEnable(true);");
 | 
				
			||||||
 | 
					                TestDemoServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            } else if (intent.getAction().equals(ACTION_DISABLE)) {
 | 
				
			||||||
 | 
					                bean.setIsEnable(false);
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "setIsEnable(false);");
 | 
				
			||||||
 | 
					                TestDemoServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (bean.isEnable()) ? START_STICKY : super.onStartCommand(intent, flags, startId);
 | 
				
			||||||
 | 
					        //return super.onStartCommand(intent, flags, startId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void run() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					        TestDemoServiceBean bean = TestDemoServiceBean.loadBean(this, TestDemoServiceBean.class);
 | 
				
			||||||
 | 
					        if (bean == null) {
 | 
				
			||||||
 | 
					            bean = new TestDemoServiceBean();
 | 
				
			||||||
 | 
					            TestDemoServiceBean.saveBean(this, bean);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (bean.isEnable()) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "run() bean.isEnable()");
 | 
				
			||||||
 | 
					            TestThread.getInstance(this).start();
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "_TestThread.start()");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "onDestroy()");
 | 
				
			||||||
 | 
					        TestThread.getInstance(this).setIsExit(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _IsRunning = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static class TestThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        volatile static TestThread _TestThread;
 | 
				
			||||||
 | 
					        Context mContext;
 | 
				
			||||||
 | 
					        volatile boolean isStarted = false;
 | 
				
			||||||
 | 
					        volatile boolean isExit = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TestThread(Context context) {
 | 
				
			||||||
 | 
					            super();
 | 
				
			||||||
 | 
					            mContext = context;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static synchronized TestThread getInstance(Context context) {
 | 
				
			||||||
 | 
					            if (_TestThread != null) {
 | 
				
			||||||
 | 
					                _TestThread.setIsExit(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _TestThread = new TestThread(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return _TestThread;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public synchronized void setIsExit(boolean isExit) {
 | 
				
			||||||
 | 
					            this.isExit = isExit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public boolean isExit() {
 | 
				
			||||||
 | 
					            return isExit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            if (isStarted == false) {
 | 
				
			||||||
 | 
					                isStarted = true;
 | 
				
			||||||
 | 
					                super.run();
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "run() start");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!isExit()) {
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        Thread.sleep(1000);
 | 
				
			||||||
 | 
					                    } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, "run() exit");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.threads;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/14 03:46:44
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import cc.winboll.studio.appbase.handlers.MainServiceHandler;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import java.lang.ref.WeakReference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MainServiceThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "MainServiceThread";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Context mContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 控制线程是否退出的标志
 | 
				
			||||||
 | 
					    volatile boolean isExist = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 服务Handler, 用于线程发送消息使用
 | 
				
			||||||
 | 
					    WeakReference<MainServiceHandler> mwrMainServiceHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsExist(boolean isExist) {
 | 
				
			||||||
 | 
					        this.isExist = isExist;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isExist() {
 | 
				
			||||||
 | 
					        return isExist;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainServiceThread(Context context, MainServiceHandler handler) {
 | 
				
			||||||
 | 
					        mContext = context;
 | 
				
			||||||
 | 
					        mwrMainServiceHandler = new WeakReference<MainServiceHandler>(handler);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (!isExist()) {
 | 
				
			||||||
 | 
					            //ToastUtils.show("run()");
 | 
				
			||||||
 | 
					            //LogUtils.d(TAG, "run()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                Thread.sleep(1000);
 | 
				
			||||||
 | 
					            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "run() exit.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,185 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.widgets;
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/15 14:41:25
 | 
				
			||||||
 | 
					 * @Describe TimeWidget
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.PendingIntent;
 | 
				
			||||||
 | 
					import android.appwidget.AppWidgetManager;
 | 
				
			||||||
 | 
					import android.appwidget.AppWidgetProvider;
 | 
				
			||||||
 | 
					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.beans.WinBollNewsBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.AppUtils;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.APPModel;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.sos.WinBoll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class APPNewsWidget extends AppWidgetProvider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "APPNewsWidget";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static final String ACTION_WAKEUP_SERVICE = APPNewsWidget.class.getName() + ".ACTION_WAKEUP_SERVICE";
 | 
				
			||||||
 | 
					    public static final String ACTION_RELOAD_REPORT = APPNewsWidget.class.getName() + ".ACTION_RELOAD_REPORT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    volatile static ArrayList<WinBollNewsBean> _WinBollNewsBeanList;
 | 
				
			||||||
 | 
					    final static int _MAX_PAGES = 10;
 | 
				
			||||||
 | 
					    final static int _OnePageLinesCount = 5;
 | 
				
			||||||
 | 
					    volatile static int _CurrentPageIndex = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
 | 
				
			||||||
 | 
					        initWinBollNewsBeanList(context);
 | 
				
			||||||
 | 
					        for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					            updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					        super.onReceive(context, intent);
 | 
				
			||||||
 | 
					        initWinBollNewsBeanList(context);
 | 
				
			||||||
 | 
					        if (intent.getAction().equals(ACTION_RELOAD_REPORT)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_RELOAD_REPORT");
 | 
				
			||||||
 | 
					            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
 | 
				
			||||||
 | 
					            int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
 | 
				
			||||||
 | 
					            for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					                updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }else if (intent.getAction().equals(ACTION_WAKEUP_SERVICE)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_WAKEUP_SERVICE");
 | 
				
			||||||
 | 
					            String szAPPModel = intent.getStringExtra(WinBoll.EXTRA_APPMODEL);
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("szAPPModel %s", szAPPModel));
 | 
				
			||||||
 | 
					            if (szAPPModel != null && !szAPPModel.equals("")) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    APPModel bean = APPModel.parseStringToBean(szAPPModel, APPModel.class);
 | 
				
			||||||
 | 
					                    if (bean != null) {
 | 
				
			||||||
 | 
					                        String szAppPackageName = bean.getAppPackageName();
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, String.format("szAppPackageName %s", szAppPackageName));
 | 
				
			||||||
 | 
					                        String szAppMainServiveName = bean.getAppMainServiveName();
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, String.format("szAppMainServiveName %s", szAppMainServiveName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        String appName = AppUtils.getAppNameByPackageName(context, szAppPackageName);
 | 
				
			||||||
 | 
					                        LogUtils.d(TAG, String.format("appName %s", appName));
 | 
				
			||||||
 | 
					                        WinBollNewsBean winBollNewsBean = new WinBollNewsBean(appName);
 | 
				
			||||||
 | 
					                        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
 | 
				
			||||||
 | 
					                        String currentTime = sdf.format(new Date());
 | 
				
			||||||
 | 
					                        StringBuilder sbLine = new StringBuilder();
 | 
				
			||||||
 | 
					                        sbLine.append("[");
 | 
				
			||||||
 | 
					                        sbLine.append(currentTime);
 | 
				
			||||||
 | 
					                        sbLine.append("] Wake up ");
 | 
				
			||||||
 | 
					                        sbLine.append(appName);
 | 
				
			||||||
 | 
					                        winBollNewsBean.setMessage(sbLine.toString());
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        addWinBollNewsBean(context, winBollNewsBean);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
 | 
				
			||||||
 | 
					                        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPNewsWidget.class));
 | 
				
			||||||
 | 
					                        for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					                            updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (IOException e) {
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, e, Thread.currentThread().getStackTrace());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    // 加入新报告信息
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    public synchronized static void addWinBollNewsBean(Context context, WinBollNewsBean bean) {
 | 
				
			||||||
 | 
					        initWinBollNewsBeanList(context);
 | 
				
			||||||
 | 
					        _WinBollNewsBeanList.add(0, bean);
 | 
				
			||||||
 | 
					        // 控制记录总数
 | 
				
			||||||
 | 
					        while (_WinBollNewsBeanList.size() > _MAX_PAGES * _OnePageLinesCount) {
 | 
				
			||||||
 | 
					            _WinBollNewsBeanList.remove(_WinBollNewsBeanList.size() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized static void initWinBollNewsBeanList(Context context) {
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList == null) {
 | 
				
			||||||
 | 
					            _WinBollNewsBeanList = new ArrayList<WinBollNewsBean>();
 | 
				
			||||||
 | 
					            WinBollNewsBean.loadBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList == null) {
 | 
				
			||||||
 | 
					            _WinBollNewsBeanList = new ArrayList<WinBollNewsBean>();
 | 
				
			||||||
 | 
					            WinBollNewsBean.saveBeanList(context, _WinBollNewsBeanList, WinBollNewsBean.class);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "updateAppWidget(...)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_news);
 | 
				
			||||||
 | 
					        //设置按钮点击事件
 | 
				
			||||||
 | 
					        Intent intentPre = new Intent(context, APPNewsWidgetClickListener.class);
 | 
				
			||||||
 | 
					        intentPre.setAction(APPNewsWidgetClickListener.ACTION_PRE);
 | 
				
			||||||
 | 
					        PendingIntent pendingIntentPre = PendingIntent.getBroadcast(context, 0, intentPre, PendingIntent.FLAG_UPDATE_CURRENT);
 | 
				
			||||||
 | 
					        views.setOnClickPendingIntent(R.id.widget_button_pre, pendingIntentPre);
 | 
				
			||||||
 | 
					        Intent intentNext = new Intent(context, APPNewsWidgetClickListener.class);
 | 
				
			||||||
 | 
					        intentNext.setAction(APPNewsWidgetClickListener.ACTION_NEXT);
 | 
				
			||||||
 | 
					        PendingIntent pendingIntentNext = PendingIntent.getBroadcast(context, 0, intentNext, PendingIntent.FLAG_UPDATE_CURRENT);
 | 
				
			||||||
 | 
					        views.setOnClickPendingIntent(R.id.widget_button_next, pendingIntentNext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        views.setTextViewText(R.id.tv_msg, getPageInfo());
 | 
				
			||||||
 | 
					        views.setTextViewText(R.id.tv_news, getMessage());
 | 
				
			||||||
 | 
					        appWidgetManager.updateAppWidget(appWidgetId, views);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String getMessage() {
 | 
				
			||||||
 | 
					        ArrayList<String> msgTemp = new ArrayList<String>();
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList != null) {
 | 
				
			||||||
 | 
					            int start = _OnePageLinesCount * _CurrentPageIndex;
 | 
				
			||||||
 | 
					            start = _WinBollNewsBeanList.size() > start ? start : _WinBollNewsBeanList.size() - 1;
 | 
				
			||||||
 | 
					            for (int i = start, j = 0; i < _WinBollNewsBeanList.size() && j < _OnePageLinesCount && start > -1; i++, j++) {
 | 
				
			||||||
 | 
					                msgTemp.add(_WinBollNewsBeanList.get(i).getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            String message = String.join("\n", msgTemp);
 | 
				
			||||||
 | 
					            return message;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void prePage(Context context) {
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList != null) {
 | 
				
			||||||
 | 
					            if (_CurrentPageIndex > 0) {
 | 
				
			||||||
 | 
					                _CurrentPageIndex = _CurrentPageIndex - 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Intent intentWidget = new Intent(context, APPNewsWidget.class);
 | 
				
			||||||
 | 
					            intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
 | 
				
			||||||
 | 
					            context.sendBroadcast(intentWidget);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void nextPage(Context context) {
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList != null) {
 | 
				
			||||||
 | 
					            if ((_CurrentPageIndex + 1) * _OnePageLinesCount < _WinBollNewsBeanList.size()) {
 | 
				
			||||||
 | 
					                _CurrentPageIndex = _CurrentPageIndex + 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Intent intentWidget = new Intent(context, APPNewsWidget.class);
 | 
				
			||||||
 | 
					            intentWidget.setAction(APPNewsWidget.ACTION_RELOAD_REPORT);
 | 
				
			||||||
 | 
					            context.sendBroadcast(intentWidget);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String getPageInfo() {
 | 
				
			||||||
 | 
					        if (_WinBollNewsBeanList == null) {
 | 
				
			||||||
 | 
					            return "0/0";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int leftCount = _WinBollNewsBeanList.size() % _OnePageLinesCount;
 | 
				
			||||||
 | 
					        int currentPageCount = _WinBollNewsBeanList.size() / _OnePageLinesCount + (leftCount == 0 ?0: 1);
 | 
				
			||||||
 | 
					        return String.format("%d/%d", _CurrentPageIndex + 1, currentPageCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.appbase.widgets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/15 17:20:46
 | 
				
			||||||
 | 
					 * @Describe WidgetButtonClickListener
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.BroadcastReceiver;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class APPNewsWidgetClickListener extends BroadcastReceiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "APPNewsWidgetClickListener";
 | 
				
			||||||
 | 
					    public static final String ACTION_PRE = APPNewsWidgetClickListener.class.getName() + ".ACTION_PRE";
 | 
				
			||||||
 | 
					    public static final String ACTION_NEXT = APPNewsWidgetClickListener.class.getName() + ".ACTION_NEXT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					        String action = intent.getAction();
 | 
				
			||||||
 | 
					        if (action == null) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("action %s", action));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (action.equals(ACTION_PRE)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_PRE");
 | 
				
			||||||
 | 
					            APPNewsWidget.prePage(context);
 | 
				
			||||||
 | 
					        } else if (action.equals(ACTION_NEXT)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_NEXT");
 | 
				
			||||||
 | 
					            APPNewsWidget.nextPage(context);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("action %s", action));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								appbase/src/main/res/drawable/ic_cloud.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								appbase/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
									
								
								appbase/src/main/res/drawable/ic_cloud_outline.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								appbase/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>
 | 
				
			||||||
@@ -1,16 +1,194 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<LinearLayout
 | 
					<LinearLayout
 | 
				
			||||||
	xmlns:android="http://schemas.android.com/apk/res/android"
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
	xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
						android:orientation="vertical"
 | 
				
			||||||
	android:layout_width="match_parent"
 | 
						android:layout_width="match_parent"
 | 
				
			||||||
	android:layout_height="match_parent"
 | 
						android:layout_height="match_parent">
 | 
				
			||||||
	android:gravity="center">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<TextView
 | 
						<androidx.appcompat.widget.Toolbar
 | 
				
			||||||
		android:layout_width="wrap_content"
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
		android:layout_height="wrap_content"
 | 
							android:layout_height="wrap_content"
 | 
				
			||||||
		android:text="Hello, WinBoll!"
 | 
							android:id="@+id/activitymainToolbar1"/>
 | 
				
			||||||
		android:onClick="onHello"/>
 | 
					
 | 
				
			||||||
 | 
						<ScrollView
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="0dp"
 | 
				
			||||||
 | 
							android:layout_weight="1.0">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<LinearLayout
 | 
				
			||||||
 | 
								xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
								android:layout_width="match_parent"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:orientation="vertical"
 | 
				
			||||||
 | 
								android:gravity="center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<LinearLayout
 | 
				
			||||||
 | 
									android:orientation="vertical"
 | 
				
			||||||
 | 
									android:layout_width="match_parent"
 | 
				
			||||||
 | 
									android:layout_height="0dp"
 | 
				
			||||||
 | 
									android:layout_weight="1.0"
 | 
				
			||||||
 | 
									android:gravity="center_horizontal">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<TextView
 | 
				
			||||||
 | 
										android:layout_width="wrap_content"
 | 
				
			||||||
 | 
										android:layout_height="wrap_content"
 | 
				
			||||||
 | 
										android:text="Hello, WinBoll!"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<TextView
 | 
				
			||||||
 | 
										android:layout_width="wrap_content"
 | 
				
			||||||
 | 
										android:layout_height="wrap_content"
 | 
				
			||||||
 | 
										android:text="Android版本10的代号是“Q”,API级别是29。   Android 10开始谷歌不再公开使用甜品作为版本代号,但内部仍保留了大量与“Q”相关的元素。Android 10本身并没有严格对应某个特定的Java版本,但在开发Android 10应用时,通常可以使用Java 8或更高版本。   Java 8为Android开发带来了诸如Lambda表达式、方法引用等新特性,能提高开发效率和代码可读性,与Android 10开发适配良好。Java 9及更高版本也可用于Android 10开发,能使用一些新的语言特性和API,但可能需要注意兼容性和配置问题。"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<LinearLayout
 | 
				
			||||||
 | 
										android:orientation="horizontal"
 | 
				
			||||||
 | 
										android:layout_width="match_parent"
 | 
				
			||||||
 | 
										android:layout_height="wrap_content"
 | 
				
			||||||
 | 
										android:gravity="right|center_vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<CheckBox
 | 
				
			||||||
 | 
											android:layout_width="wrap_content"
 | 
				
			||||||
 | 
											android:layout_height="wrap_content"
 | 
				
			||||||
 | 
											android:text="Debug Mode"
 | 
				
			||||||
 | 
											android:layout_weight="1.0"
 | 
				
			||||||
 | 
											android:onClick="onSwitchDebugMode"
 | 
				
			||||||
 | 
											android:id="@+id/activitymainCheckBox1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<Button
 | 
				
			||||||
 | 
											android:layout_width="wrap_content"
 | 
				
			||||||
 | 
											android:layout_height="wrap_content"
 | 
				
			||||||
 | 
											android:text="Test Application CrashReport"
 | 
				
			||||||
 | 
											android:textAllCaps="false"
 | 
				
			||||||
 | 
											android:onClick="onTestApplicationCrashReport"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<ScrollView
 | 
				
			||||||
 | 
										android:layout_width="match_parent"
 | 
				
			||||||
 | 
										android:layout_height="400dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<LinearLayout
 | 
				
			||||||
 | 
											android:orientation="vertical"
 | 
				
			||||||
 | 
											android:layout_width="match_parent"
 | 
				
			||||||
 | 
											android:layout_height="wrap_content"
 | 
				
			||||||
 | 
											android:gravity="right">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<HorizontalScrollView
 | 
				
			||||||
 | 
												android:layout_width="match_parent"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												<LinearLayout
 | 
				
			||||||
 | 
													android:orientation="horizontal"
 | 
				
			||||||
 | 
													android:layout_width="wrap_content"
 | 
				
			||||||
 | 
													android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													<Button
 | 
				
			||||||
 | 
														android:layout_width="wrap_content"
 | 
				
			||||||
 | 
														android:layout_height="wrap_content"
 | 
				
			||||||
 | 
														android:text="SartTestDemoService"
 | 
				
			||||||
 | 
														android:textAllCaps="false"
 | 
				
			||||||
 | 
														android:onClick="onSartTestDemoService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													<Button
 | 
				
			||||||
 | 
														android:layout_width="wrap_content"
 | 
				
			||||||
 | 
														android:layout_height="wrap_content"
 | 
				
			||||||
 | 
														android:text="StopTestDemoService"
 | 
				
			||||||
 | 
														android:textAllCaps="false"
 | 
				
			||||||
 | 
														android:onClick="onStopTestDemoService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
													<Button
 | 
				
			||||||
 | 
														android:layout_width="wrap_content"
 | 
				
			||||||
 | 
														android:layout_height="wrap_content"
 | 
				
			||||||
 | 
														android:text="StopTestDemoServiceNoSettings"
 | 
				
			||||||
 | 
														android:textAllCaps="false"
 | 
				
			||||||
 | 
														android:onClick="onStopTestDemoServiceNoSettings"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											</HorizontalScrollView>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        <HorizontalScrollView
 | 
				
			||||||
 | 
					                            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                            android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <LinearLayout
 | 
				
			||||||
 | 
					                                android:orientation="horizontal"
 | 
				
			||||||
 | 
					                                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                                android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <Button
 | 
				
			||||||
 | 
					                                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                                    android:text="SartTestDemoBindService"
 | 
				
			||||||
 | 
					                                    android:textAllCaps="false"
 | 
				
			||||||
 | 
					                                    android:onClick="onSartTestDemoBindService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <Button
 | 
				
			||||||
 | 
					                                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                                    android:text="StopTestDemoBindService"
 | 
				
			||||||
 | 
					                                    android:textAllCaps="false"
 | 
				
			||||||
 | 
					                                    android:onClick="onStopTestDemoBindService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <Button
 | 
				
			||||||
 | 
					                                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                                    android:text="StopTestDemoBindServiceNoSettings"
 | 
				
			||||||
 | 
					                                    android:textAllCaps="false"
 | 
				
			||||||
 | 
					                                    android:onClick="onStopTestDemoBindServiceNoSettings"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											</HorizontalScrollView>
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												android:layout_width="wrap_content"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content"
 | 
				
			||||||
 | 
												android:text="StartCenter"
 | 
				
			||||||
 | 
												android:textAllCaps="false"
 | 
				
			||||||
 | 
												android:onClick="onStartCenter"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												android:layout_width="wrap_content"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content"
 | 
				
			||||||
 | 
												android:text="StopCenter"
 | 
				
			||||||
 | 
												android:textAllCaps="false"
 | 
				
			||||||
 | 
												android:onClick="onStopCenter"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												android:layout_width="wrap_content"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content"
 | 
				
			||||||
 | 
												android:text="TestStopMainServiceWithoutSettingEnable"
 | 
				
			||||||
 | 
												android:textAllCaps="false"
 | 
				
			||||||
 | 
												android:onClick="onTestStopMainServiceWithoutSettingEnable"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												android:layout_width="wrap_content"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content"
 | 
				
			||||||
 | 
												android:text="TestUseComponentStartService"
 | 
				
			||||||
 | 
												android:textAllCaps="false"
 | 
				
			||||||
 | 
												android:onClick="onTestUseComponentStartService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<Button
 | 
				
			||||||
 | 
												android:layout_width="wrap_content"
 | 
				
			||||||
 | 
												android:layout_height="wrap_content"
 | 
				
			||||||
 | 
												android:text="TestSOS"
 | 
				
			||||||
 | 
												android:textAllCaps="false"
 | 
				
			||||||
 | 
												android:onClick="onTestSOS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									</ScrollView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<cc.winboll.studio.libappbase.LogView
 | 
				
			||||||
 | 
										android:layout_height="500dp"
 | 
				
			||||||
 | 
										android:layout_width="match_parent"
 | 
				
			||||||
 | 
										android:id="@+id/activitymainLogView1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</ScrollView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</LinearLayout>
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								appbase/src/main/res/layout/widget_news.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								appbase/src/main/res/layout/widget_news.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
						android:layout_width="match_parent"
 | 
				
			||||||
 | 
						android:layout_height="match_parent"
 | 
				
			||||||
 | 
						android:orientation="vertical"
 | 
				
			||||||
 | 
						android:background="#FFFFFFFF">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<LinearLayout
 | 
				
			||||||
 | 
							android:orientation="horizontal"
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="wrap_content"
 | 
				
			||||||
 | 
							android:gravity="right|center_vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<TextView
 | 
				
			||||||
 | 
								android:layout_width="0dp"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:id="@+id/tv_title"
 | 
				
			||||||
 | 
								android:layout_weight="1.0"
 | 
				
			||||||
 | 
								android:text="WinBollNews"
 | 
				
			||||||
 | 
								android:textStyle="bold"
 | 
				
			||||||
 | 
								android:textSize="16sp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<Button
 | 
				
			||||||
 | 
								android:layout_width="48dp"
 | 
				
			||||||
 | 
								android:layout_height="48dp"
 | 
				
			||||||
 | 
								android:text="⇦"
 | 
				
			||||||
 | 
								android:id="@+id/widget_button_pre"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<Button
 | 
				
			||||||
 | 
								android:layout_width="48dp"
 | 
				
			||||||
 | 
								android:layout_height="48dp"
 | 
				
			||||||
 | 
								android:text="⇨"
 | 
				
			||||||
 | 
								android:id="@+id/widget_button_next"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:id="@+id/tv_msg"/>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
						<TextView
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="0dp"
 | 
				
			||||||
 | 
							android:id="@+id/tv_news"
 | 
				
			||||||
 | 
							android:layout_weight="1.0"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,9 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<resources>
 | 
					 | 
				
			||||||
    <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
 | 
					 | 
				
			||||||
        <item name="android:colorPrimary">@color/colorPrimary</item>
 | 
					 | 
				
			||||||
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
 | 
					 | 
				
			||||||
        <item name="android:colorAccent">@color/colorAccent</item>
 | 
					 | 
				
			||||||
        <item name="android:navigationBarColor">?android:colorPrimary</item>
 | 
					 | 
				
			||||||
	</style>
 | 
					 | 
				
			||||||
</resources>
 | 
					 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <color name="colorPrimary">#009688</color>
 | 
					    <color name="colorPrimary">#005800FF</color>
 | 
				
			||||||
    <color name="colorPrimaryDark">#00796B</color>
 | 
					    <color name="colorPrimaryDark">#005800FF</color>
 | 
				
			||||||
    <color name="colorAccent">#FF9800</color>
 | 
					    <color name="colorAccent">#005800FF</color>
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <string name="app_name">AppBase</string>
 | 
					    <string name="app_name">AppBase</string>
 | 
				
			||||||
</resources>
 | 
					    <string name="tileservice_name">WinBoll</string>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,14 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <style name="AppTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
 | 
					    <style name="AppTheme" parent="APPBaseTheme">
 | 
				
			||||||
	</style>
 | 
					        <item name="attrColorPrimary">@color/colorPrimary</item>
 | 
				
			||||||
</resources>
 | 
					        <item name="themeGlobalCrashActivity">@style/MyGlobalCrashActivityTheme</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="MyGlobalCrashActivityTheme" parent="GlobalCrashActivityTheme">
 | 
				
			||||||
 | 
					        <item name="colorTittle">#FFFFFFFF</item>
 | 
				
			||||||
 | 
					        <item name="colorTittleBackgound">#FF00A4B3</item>
 | 
				
			||||||
 | 
					        <item name="colorText">#FFFFFFFF</item>
 | 
				
			||||||
 | 
					        <item name="colorTextBackgound">#FF000000</item>
 | 
				
			||||||
 | 
						</style>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								appbase/src/main/res/xml/widget_provider_info_sos.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								appbase/src/main/res/xml/widget_provider_info_sos.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:minWidth="200dp"
 | 
				
			||||||
 | 
					    android:minHeight="100dp"
 | 
				
			||||||
 | 
					    android:updatePeriodMillis="1000"
 | 
				
			||||||
 | 
					    android:initialLayout="@layout/widget_news">
 | 
				
			||||||
 | 
					</appwidget-provider>
 | 
				
			||||||
							
								
								
									
										37
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								build.gradle
									
									
									
									
									
								
							@@ -1,8 +1,22 @@
 | 
				
			|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
 | 
					// Top-level build file where you can add configuration options common to all sub-projects/modules.
 | 
				
			||||||
buildscript {
 | 
					buildscript {
 | 
				
			||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        google()
 | 
					        maven { url 'https://maven.aliyun.com/repository/public/' } 
 | 
				
			||||||
 | 
					        maven { url 'https://maven.aliyun.com/repository/google/' }
 | 
				
			||||||
 | 
					        maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
 | 
				
			||||||
 | 
					        maven { url 'https://dl.bintray.com/ppartisan/maven/' }
 | 
				
			||||||
 | 
					        maven { url "https://clojars.org/repo/" }
 | 
				
			||||||
 | 
					        maven { url "https://jitpack.io" }
 | 
				
			||||||
        mavenCentral()
 | 
					        mavenCentral()
 | 
				
			||||||
 | 
					        google()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Nexus Maven 库地址
 | 
				
			||||||
 | 
					        // "WinBoll Release"
 | 
				
			||||||
 | 
					        maven { url "https://nexus.winboll.cc/repository/maven-public/" }
 | 
				
			||||||
 | 
					        // "WinBoll Snapshot"
 | 
				
			||||||
 | 
					        maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        mavenLocal()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    dependencies {
 | 
					    dependencies {
 | 
				
			||||||
        classpath 'com.android.tools.build:gradle:7.2.1'
 | 
					        classpath 'com.android.tools.build:gradle:7.2.1'
 | 
				
			||||||
@@ -13,12 +27,6 @@ buildscript {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
allprojects {
 | 
					allprojects {
 | 
				
			||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        // Nexus Maven 库地址
 | 
					 | 
				
			||||||
        // "WinBoll Release"
 | 
					 | 
				
			||||||
        maven { url "https://nexus.winboll.cc/repository/maven-public/" }
 | 
					 | 
				
			||||||
        // "WinBoll Snapshot"
 | 
					 | 
				
			||||||
        maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        maven { url 'https://maven.aliyun.com/repository/public/' } 
 | 
					        maven { url 'https://maven.aliyun.com/repository/public/' } 
 | 
				
			||||||
        maven { url 'https://maven.aliyun.com/repository/google/' }
 | 
					        maven { url 'https://maven.aliyun.com/repository/google/' }
 | 
				
			||||||
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
 | 
					        maven { url 'https://maven.aliyun.com/repository/gradle-plugin/' }
 | 
				
			||||||
@@ -27,6 +35,13 @@ allprojects {
 | 
				
			|||||||
        maven { url "https://jitpack.io" }
 | 
					        maven { url "https://jitpack.io" }
 | 
				
			||||||
        mavenCentral()
 | 
					        mavenCentral()
 | 
				
			||||||
        google()
 | 
					        google()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Nexus Maven 库地址
 | 
				
			||||||
 | 
					        // "WinBoll Release"
 | 
				
			||||||
 | 
					        maven { url "https://nexus.winboll.cc/repository/maven-public/" }
 | 
				
			||||||
 | 
					        // "WinBoll Snapshot"
 | 
				
			||||||
 | 
					        maven { url "https://nexus.winboll.cc/repository/maven-snapshots/" }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        mavenLocal()
 | 
					        mavenLocal()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ext {
 | 
					    ext {
 | 
				
			||||||
@@ -69,6 +84,14 @@ allprojects {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    subprojects {
 | 
				
			||||||
 | 
					        tasks.withType(JavaCompile) {
 | 
				
			||||||
 | 
					            options.compilerArgs << "-parameters"
 | 
				
			||||||
 | 
					            sourceCompatibility = JavaVersion.VERSION_1_8
 | 
				
			||||||
 | 
					            targetCompatibility = JavaVersion.VERSION_1_8
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
task clean(type: Delete) {
 | 
					task clean(type: Delete) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								contacts/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								contacts/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					# Contacts
 | 
				
			||||||
 | 
					源码参考自:
 | 
				
			||||||
 | 
					https://github.com/aJIEw/PhoneCallApp.git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### 介绍
 | 
				
			||||||
 | 
					通讯录与拨号
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### 软件架构
 | 
				
			||||||
 | 
					适配安卓应用 [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/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### 参考文档
 | 
				
			||||||
							
								
								
									
										1
									
								
								contacts/app_update_description.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contacts/app_update_description.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										71
									
								
								contacts/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								contacts/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -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.contacts"
 | 
				
			||||||
 | 
					        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'])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								contacts/build.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								contacts/build.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					#Created by .winboll/winboll_app_build.gradle
 | 
				
			||||||
 | 
					#Tue Feb 25 00:17:29 HKT 2025
 | 
				
			||||||
 | 
					stageCount=2
 | 
				
			||||||
 | 
					libraryProject=
 | 
				
			||||||
 | 
					baseVersion=1.0
 | 
				
			||||||
 | 
					publishVersion=1.0.1
 | 
				
			||||||
 | 
					buildCount=0
 | 
				
			||||||
 | 
					baseBetaVersion=1.0.2
 | 
				
			||||||
							
								
								
									
										17
									
								
								contacts/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contacts/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					# Add project specific ProGuard rules here.
 | 
				
			||||||
 | 
					# By default, the flags in this file are appended to flags specified
 | 
				
			||||||
 | 
					# in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt
 | 
				
			||||||
 | 
					# You can edit the include path and order by changing the proguardFiles
 | 
				
			||||||
 | 
					# directive in build.gradle.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# For more details, see
 | 
				
			||||||
 | 
					#   http://developer.android.com/guide/developing/tools/proguard.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Add any project specific keep options here:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If your project uses WebView with JS, uncomment the following
 | 
				
			||||||
 | 
					# and specify the fully qualified class name to the JavaScript interface
 | 
				
			||||||
 | 
					# class:
 | 
				
			||||||
 | 
					#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 | 
				
			||||||
 | 
					#   public *;
 | 
				
			||||||
 | 
					#}
 | 
				
			||||||
							
								
								
									
										13
									
								
								contacts/src/beta/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								contacts/src/beta/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools" >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <application
 | 
				
			||||||
 | 
					        tools:replace="android:icon"
 | 
				
			||||||
 | 
					        android:icon="@drawable/ic_winbollbeta">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Put flavor specific code here -->
 | 
				
			||||||
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</manifest>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								contacts/src/beta/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contacts/src/beta/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <string name="app_name">Contacts+</string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
							
								
								
									
										187
									
								
								contacts/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								contacts/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					<?xml version='1.0' encoding='utf-8'?>
 | 
				
			||||||
 | 
					<manifest
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    package="cc.winboll.studio.contacts">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- BIND_AUTOFILL_SERVICE -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.BIND_AUTOFILL_SERVICE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 拨打电话 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.CALL_PHONE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 读取手机状态和身份 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 修改系统设置 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 重新设置外拨电话的路径 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 读取联系人 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.READ_CONTACTS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 修改您的通讯录 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 此应用可显示在其他应用上方 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 更改您的音频设置 -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    <application
 | 
				
			||||||
 | 
					        android:name=".App"
 | 
				
			||||||
 | 
					        android:allowBackup="true"
 | 
				
			||||||
 | 
					        android:icon="@drawable/ic_winboll"
 | 
				
			||||||
 | 
					        android:label="@string/app_name"
 | 
				
			||||||
 | 
					        android:theme="@style/MyAppTheme"
 | 
				
			||||||
 | 
					        android:supportsRtl="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".MainActivity"
 | 
				
			||||||
 | 
					            android:label="@string/app_name"
 | 
				
			||||||
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.MAIN"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.LAUNCHER"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </activity>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".activities.CallActivity"
 | 
				
			||||||
 | 
					            android:label="CallActivity"
 | 
				
			||||||
 | 
					            android:launchMode="singleTask"
 | 
				
			||||||
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </activity>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".phonecallui.PhoneCallActivity"
 | 
				
			||||||
 | 
					            android:launchMode="singleInstance"
 | 
				
			||||||
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.DIAL"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.VIEW"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.DEFAULT"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.BROWSABLE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <data android:scheme="tel"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.DIAL"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.DEFAULT"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </activity>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity android:name="cc.winboll.studio.contacts.activities.SettingsActivity"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name="cc.winboll.studio.contacts.services.MainService"
 | 
				
			||||||
 | 
					            android:exported="true"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service android:name="cc.winboll.studio.contacts.services.AssistantService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name=".phonecallui.PhoneCallService"
 | 
				
			||||||
 | 
					            android:permission="android.permission.BIND_INCALL_SERVICE"
 | 
				
			||||||
 | 
					            android:exported="false">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <meta-data
 | 
				
			||||||
 | 
					                android:name="android.telecom.IN_CALL_SERVICE_UI"
 | 
				
			||||||
 | 
					                android:value="true"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.telecom.InCallService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </service>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name=".listenphonecall.CallListenerService"
 | 
				
			||||||
 | 
					            android:enabled="true"
 | 
				
			||||||
 | 
					            android:exported="false">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter android:priority="1000">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name=".service.CallShowService"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </service>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver android:name=".receivers.MainReceiver">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.contacts.receivers.MainReceiver"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver
 | 
				
			||||||
 | 
					            android:name=".widgets.APPStatusWidget"
 | 
				
			||||||
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <meta-data
 | 
				
			||||||
 | 
					                android:name="android.appwidget.provider"
 | 
				
			||||||
 | 
					                android:resource="@xml/appwidget_provider_info"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <receiver android:name=".widgets.APPStatusWidgetClickListener">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <action android:name="cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </receiver>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <provider
 | 
				
			||||||
 | 
					            android:name="androidx.core.content.FileProvider"
 | 
				
			||||||
 | 
					            android:authorities="${applicationId}.fileprovider"
 | 
				
			||||||
 | 
					            android:exported="false"
 | 
				
			||||||
 | 
					            android:grantUriPermissions="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <meta-data
 | 
				
			||||||
 | 
					                android:name="android.support.FILE_PROVIDER_PATHS"
 | 
				
			||||||
 | 
					                android:resource="@xml/studio_provider"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </provider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</manifest>
 | 
				
			||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Activity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ActivityStack {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final ActivityStack INSTANCE = new ActivityStack();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<Activity> activities = new ArrayList<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static ActivityStack getInstance() {
 | 
				
			||||||
 | 
					        return INSTANCE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addActivity(Activity activity) {
 | 
				
			||||||
 | 
					        activities.add(activity);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Activity getTopActivity() {
 | 
				
			||||||
 | 
					        if (activities.isEmpty()) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return activities.get(activities.size() - 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void finishTopActivity() {
 | 
				
			||||||
 | 
					        if (!activities.isEmpty()) {
 | 
				
			||||||
 | 
					            activities.remove(activities.size() - 1).finish();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void finishActivity(Activity activity) {
 | 
				
			||||||
 | 
					        if (activity != null) {
 | 
				
			||||||
 | 
					            activities.remove(activity);
 | 
				
			||||||
 | 
					            activity.finish();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void finishActivity(Class activityClass) {
 | 
				
			||||||
 | 
					        for (Activity activity : activities) {
 | 
				
			||||||
 | 
					            if (activity.getClass().equals(activityClass)) {
 | 
				
			||||||
 | 
					                finishActivity(activity);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void finishAllActivity() {
 | 
				
			||||||
 | 
					        if (!activities.isEmpty()) {
 | 
				
			||||||
 | 
					            for (Activity activity : activities) {
 | 
				
			||||||
 | 
					                activity.finish();
 | 
				
			||||||
 | 
					                activities.remove(activity);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								contacts/src/main/java/cc/winboll/studio/contacts/App.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								contacts/src/main/java/cc/winboll/studio/contacts/App.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @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");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,406 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.Manifest;
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint;
 | 
				
			||||||
 | 
					import android.app.Activity;
 | 
				
			||||||
 | 
					import android.app.ActivityManager;
 | 
				
			||||||
 | 
					import android.app.role.RoleManager;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.pm.PackageManager;
 | 
				
			||||||
 | 
					import android.net.Uri;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.provider.Settings;
 | 
				
			||||||
 | 
					import android.telecom.TelecomManager;
 | 
				
			||||||
 | 
					import android.view.LayoutInflater;
 | 
				
			||||||
 | 
					import android.view.Menu;
 | 
				
			||||||
 | 
					import android.view.MenuItem;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.view.WindowManager;
 | 
				
			||||||
 | 
					import android.widget.CheckBox;
 | 
				
			||||||
 | 
					import android.widget.CompoundButton;
 | 
				
			||||||
 | 
					import android.widget.ImageView;
 | 
				
			||||||
 | 
					import android.widget.LinearLayout;
 | 
				
			||||||
 | 
					import android.widget.Switch;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AlertDialog;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import androidx.appcompat.widget.Toolbar;
 | 
				
			||||||
 | 
					import androidx.core.content.ContextCompat;
 | 
				
			||||||
 | 
					import androidx.viewpager.widget.ViewPager;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.activities.CallActivity;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.adapters.MyPagerAdapter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.beans.MainServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.services.MainService;
 | 
				
			||||||
 | 
					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.contacts.listenphonecall.CallListenerService;
 | 
				
			||||||
 | 
					import com.google.android.material.tabs.TabLayout;
 | 
				
			||||||
 | 
					import java.lang.reflect.Field;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import android.content.DialogInterface;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.activities.SettingsActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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<View> 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_call) {
 | 
				
			||||||
 | 
					            Intent intent = new Intent(this, CallActivity.class);
 | 
				
			||||||
 | 
					            startActivity(intent);
 | 
				
			||||||
 | 
					            //WinBollActivityManager.getInstance(this).startWinBollActivity(this, CallActivity.class);
 | 
				
			||||||
 | 
					        } else 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();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/20 21:14:52
 | 
				
			||||||
 | 
					 * @Describe PhoneCallManager
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.telecom.Call;
 | 
				
			||||||
 | 
					import android.telecom.VideoProfile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PhoneCallManager {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static final String TAG = "PhoneCallManager";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static Call call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 接听电话
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void answer() {
 | 
				
			||||||
 | 
					        if (call != null) {
 | 
				
			||||||
 | 
					            call.answer(VideoProfile.STATE_AUDIO_ONLY);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 断开电话,包括来电时的拒接以及接听后的挂断
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void disconnect() {
 | 
				
			||||||
 | 
					        if (call != null) {
 | 
				
			||||||
 | 
					            call.disconnect();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.activities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/20 17:15:46
 | 
				
			||||||
 | 
					 * @Describe 拨号窗口
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.Manifest;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.pm.PackageManager;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.telephony.PhoneStateListener;
 | 
				
			||||||
 | 
					import android.telephony.TelephonyManager;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.widget.Button;
 | 
				
			||||||
 | 
					import android.widget.EditText;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					import androidx.annotation.NonNull;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import androidx.core.app.ActivityCompat;
 | 
				
			||||||
 | 
					import androidx.core.content.ContextCompat;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.MainActivity;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CallActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					    public static final String TAG = "CallActivity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int REQUEST_CALL_PHONE = 1;
 | 
				
			||||||
 | 
					    private EditText phoneNumberEditText;
 | 
				
			||||||
 | 
					    private TextView callStatusTextView;
 | 
				
			||||||
 | 
					    private TelephonyManager telephonyManager;
 | 
				
			||||||
 | 
					    private MyPhoneStateListener phoneStateListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        //setContentView(R.layout.activity_main);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        phoneNumberEditText = findViewById(R.id.phone_number);
 | 
				
			||||||
 | 
					        Button dialButton = findViewById(R.id.dial_button);
 | 
				
			||||||
 | 
					        callStatusTextView = findViewById(R.id.call_status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dialButton.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onClick(View v) {
 | 
				
			||||||
 | 
					                    String phoneNumber = phoneNumberEditText.getText().toString().trim();
 | 
				
			||||||
 | 
					                    if (!phoneNumber.isEmpty()) {
 | 
				
			||||||
 | 
					                        if (ContextCompat.checkSelfPermission(CallActivity.this, Manifest.permission.CALL_PHONE)
 | 
				
			||||||
 | 
					                            != PackageManager.PERMISSION_GRANTED) {
 | 
				
			||||||
 | 
					                            ActivityCompat.requestPermissions(CallActivity.this,
 | 
				
			||||||
 | 
					                                                              new String[]{Manifest.permission.CALL_PHONE},
 | 
				
			||||||
 | 
					                                                              REQUEST_CALL_PHONE);
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            dialPhoneNumber(phoneNumber);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        Toast.makeText(CallActivity.this, "请输入电话号码", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 初始化TelephonyManager和PhoneStateListener
 | 
				
			||||||
 | 
					        telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
 | 
				
			||||||
 | 
					        phoneStateListener = new MyPhoneStateListener();
 | 
				
			||||||
 | 
					        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
 | 
				
			||||||
 | 
					        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 | 
				
			||||||
 | 
					        if (requestCode == REQUEST_CALL_PHONE) {
 | 
				
			||||||
 | 
					            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 | 
				
			||||||
 | 
					                String phoneNumber = phoneNumberEditText.getText().toString().trim();
 | 
				
			||||||
 | 
					                dialPhoneNumber(phoneNumber);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Toast.makeText(this, "未授予拨打电话权限", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void dialPhoneNumber(String phoneNumber) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(Intent.ACTION_CALL);
 | 
				
			||||||
 | 
					        intent.setData(android.net.Uri.parse("tel:" + phoneNumber));
 | 
				
			||||||
 | 
					        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        startActivity(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private class MyPhoneStateListener extends PhoneStateListener {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onCallStateChanged(int state, String incomingNumber) {
 | 
				
			||||||
 | 
					            switch (state) {
 | 
				
			||||||
 | 
					                case TelephonyManager.CALL_STATE_IDLE:
 | 
				
			||||||
 | 
					                    callStatusTextView.setText("电话已挂断");
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case TelephonyManager.CALL_STATE_OFFHOOK:
 | 
				
			||||||
 | 
					                    callStatusTextView.setText("正在通话中");
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case TelephonyManager.CALL_STATE_RINGING:
 | 
				
			||||||
 | 
					                    callStatusTextView.setText("来电: " + incomingNumber);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					        // 取消监听
 | 
				
			||||||
 | 
					        if (telephonyManager != null) {
 | 
				
			||||||
 | 
					            telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.activities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/20 20:18:26
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.net.Uri;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.widget.Button;
 | 
				
			||||||
 | 
					import android.widget.EditText;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DialerActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "DialerActivity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private EditText phoneNumberEditText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_dialer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        phoneNumberEditText = findViewById(R.id.phone_number_edit_text);
 | 
				
			||||||
 | 
					        Button dialButton = findViewById(R.id.dial_button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dialButton.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onClick(View v) {
 | 
				
			||||||
 | 
					                    String phoneNumber = phoneNumberEditText.getText().toString();
 | 
				
			||||||
 | 
					                    Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
 | 
				
			||||||
 | 
					                    startActivity(intent);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.activities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/21 05:37:42
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.NotificationManager;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.DialogInterface;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.media.AudioManager;
 | 
				
			||||||
 | 
					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.Switch;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AlertDialog;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import androidx.appcompat.widget.Toolbar;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.beans.RingTongBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.IWinBollActivity;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.bean.APPInfo;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					import java.lang.reflect.Field;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SettingsActivity extends AppCompatActivity implements IWinBollActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "SettingsActivity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Toolbar mToolbar;
 | 
				
			||||||
 | 
					    Switch swSilent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.adapters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/20 14:20:38
 | 
				
			||||||
 | 
					 * @Describe ImagePagerAdapter
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ImagePagerAdapter {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static final String TAG = "ImagePagerAdapter";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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.contacts.fragments.CallFragment;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.fragments.ContactsFragment;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.beans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/21 09:52:10
 | 
				
			||||||
 | 
					 * @Describe 电话黑名单规则
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.util.JsonReader;
 | 
				
			||||||
 | 
					import android.util.JsonWriter;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.BaseBean;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PhoneBlackRuleBean extends BaseBean {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static final String TAG = "PhoneBlackRuleBean";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    String ruleText;
 | 
				
			||||||
 | 
					    boolean isEnable;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public PhoneBlackRuleBean() {
 | 
				
			||||||
 | 
					        this.ruleText = "";
 | 
				
			||||||
 | 
					        this.isEnable = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PhoneBlackRuleBean(String ruleText, boolean isEnable) {
 | 
				
			||||||
 | 
					        this.ruleText = ruleText;
 | 
				
			||||||
 | 
					        this.isEnable = isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRuleText(String ruleText) {
 | 
				
			||||||
 | 
					        this.ruleText = ruleText;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getRuleText() {
 | 
				
			||||||
 | 
					        return ruleText;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIsEnable(boolean isEnable) {
 | 
				
			||||||
 | 
					        this.isEnable = isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isEnable() {
 | 
				
			||||||
 | 
					        return isEnable;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return PhoneBlackRuleBean.class.getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
				
			||||||
 | 
					        super.writeThisToJsonWriter(jsonWriter);
 | 
				
			||||||
 | 
					        jsonWriter.name("ruleText").value(getRuleText());
 | 
				
			||||||
 | 
					        jsonWriter.name("isEnable").value(isEnable());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
				
			||||||
 | 
					        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
				
			||||||
 | 
					            if (name.equals("ruleText")) {
 | 
				
			||||||
 | 
					                setRuleText(jsonReader.nextString());
 | 
				
			||||||
 | 
					            } 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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.beans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/24 18:47:11
 | 
				
			||||||
 | 
					 * @Describe 手机铃声设置参数类
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.BaseBean;
 | 
				
			||||||
 | 
					import android.util.JsonWriter;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import android.media.AudioManager;
 | 
				
			||||||
 | 
					import android.util.JsonReader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RingTongBean extends BaseBean {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static final String TAG = "AudioRingTongBean";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // 模式
 | 
				
			||||||
 | 
					    int ringerMode;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public RingTongBean() {
 | 
				
			||||||
 | 
					        this.ringerMode = AudioManager.RINGER_MODE_NORMAL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public RingTongBean(int ringerMode) {
 | 
				
			||||||
 | 
					        this.ringerMode = ringerMode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setRingerMode(int ringerMode) {
 | 
				
			||||||
 | 
					        this.ringerMode = ringerMode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getRingerMode() {
 | 
				
			||||||
 | 
					        return ringerMode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getName() {
 | 
				
			||||||
 | 
					        return RingTongBean.class.getName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeThisToJsonWriter(JsonWriter jsonWriter) throws IOException {
 | 
				
			||||||
 | 
					        super.writeThisToJsonWriter(jsonWriter);
 | 
				
			||||||
 | 
					        jsonWriter.name("ringerMode").value(getRingerMode());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean initObjectsFromJsonReader(JsonReader jsonReader, String name) throws IOException {
 | 
				
			||||||
 | 
					        if (super.initObjectsFromJsonReader(jsonReader, name)) { return true; } else {
 | 
				
			||||||
 | 
					            if (name.equals("ringerMode")) {
 | 
				
			||||||
 | 
					                setRingerMode(jsonReader.nextInt());
 | 
				
			||||||
 | 
					            } 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,76 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.dun;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/21 06:15:10
 | 
				
			||||||
 | 
					 * @Describe 云盾防御规则
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.beans.PhoneBlackRuleBean;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Rules {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "Rules";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ArrayList<PhoneBlackRuleBean> _PhoneBlacRuleBeanList;
 | 
				
			||||||
 | 
					    static volatile Rules _Rules;
 | 
				
			||||||
 | 
					    Context mContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Rules(Context context) {
 | 
				
			||||||
 | 
					        mContext = context;
 | 
				
			||||||
 | 
					        _PhoneBlacRuleBeanList = new ArrayList<PhoneBlackRuleBean>();
 | 
				
			||||||
 | 
					        PhoneBlackRuleBean.loadBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    public static synchronized Rules getInstance(Context context) {
 | 
				
			||||||
 | 
					        if (_Rules == null) {
 | 
				
			||||||
 | 
					            _Rules = new Rules(context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return _Rules;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isAllowed(String phoneNumber) {
 | 
				
			||||||
 | 
					        // 黑名单拒接
 | 
				
			||||||
 | 
					        for (int i = 0; i < _PhoneBlacRuleBeanList.size(); i++) {
 | 
				
			||||||
 | 
					            if (_PhoneBlacRuleBeanList.get(i).isEnable()) {
 | 
				
			||||||
 | 
					                String regex = _PhoneBlacRuleBeanList.get(i).getRuleText();
 | 
				
			||||||
 | 
					                if (Pattern.matches(regex, phoneNumber)) {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 手机号码允许
 | 
				
			||||||
 | 
					        // 中国手机号码正则表达式,以1开头,第二位可以是3、4、5、6、7、8、9,后面跟9位数字
 | 
				
			||||||
 | 
					        String regex = "^1[3-9]\\d{9}$";
 | 
				
			||||||
 | 
					        if (Pattern.matches(regex, phoneNumber)) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 指定区号号码允许
 | 
				
			||||||
 | 
					        regex = "^0660\\d+$";
 | 
				
			||||||
 | 
					        if (Pattern.matches(regex, phoneNumber)) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 指定区号号码允许
 | 
				
			||||||
 | 
					        regex = "^020\\d+$";
 | 
				
			||||||
 | 
					        if (Pattern.matches(regex, phoneNumber)) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 其他拒接
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void add(String phoneRuleBlack, boolean isEnable) {
 | 
				
			||||||
 | 
					        _PhoneBlacRuleBeanList.add(new PhoneBlackRuleBean(phoneRuleBlack, isEnable));
 | 
				
			||||||
 | 
					        PhoneBlackRuleBean.saveBeanList(mContext, _PhoneBlacRuleBeanList, PhoneBlackRuleBean.class);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ArrayList<PhoneBlackRuleBean> getPhoneBlacRuleBeanList() {
 | 
				
			||||||
 | 
					        return _PhoneBlacRuleBeanList;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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.contacts.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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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.contacts.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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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.contacts.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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.handlers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/14 03:51:40
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Message;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.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<MainService> serviceWeakReference;
 | 
				
			||||||
 | 
					    public MainServiceHandler(MainService service) {
 | 
				
			||||||
 | 
					        serviceWeakReference = new WeakReference<MainService>(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;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,206 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.listenphonecall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Service;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.pm.ActivityInfo;
 | 
				
			||||||
 | 
					import android.graphics.PixelFormat;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					import android.os.IBinder;
 | 
				
			||||||
 | 
					import android.telephony.PhoneStateListener;
 | 
				
			||||||
 | 
					import android.telephony.TelephonyManager;
 | 
				
			||||||
 | 
					import android.text.TextUtils;
 | 
				
			||||||
 | 
					import android.view.Gravity;
 | 
				
			||||||
 | 
					import android.view.KeyEvent;
 | 
				
			||||||
 | 
					import android.view.LayoutInflater;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.view.WindowManager;
 | 
				
			||||||
 | 
					import android.widget.Button;
 | 
				
			||||||
 | 
					import android.widget.FrameLayout;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import androidx.annotation.Nullable;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.MainActivity;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CallListenerService extends Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private View phoneCallView;
 | 
				
			||||||
 | 
					    private TextView tvCallNumber;
 | 
				
			||||||
 | 
					    private Button btnOpenApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private WindowManager windowManager;
 | 
				
			||||||
 | 
					    private WindowManager.LayoutParams params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private PhoneStateListener phoneStateListener;
 | 
				
			||||||
 | 
					    private TelephonyManager telephonyManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String callNumber;
 | 
				
			||||||
 | 
					    private boolean hasShown;
 | 
				
			||||||
 | 
					    private boolean isCallingIn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initPhoneStateListener();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initPhoneCallView();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Nullable
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public IBinder onBind(Intent intent) {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 初始化来电状态监听器
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void initPhoneStateListener() {
 | 
				
			||||||
 | 
					        phoneStateListener = new PhoneStateListener() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onCallStateChanged(int state, String incomingNumber) {
 | 
				
			||||||
 | 
					                super.onCallStateChanged(state, incomingNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                callNumber = incomingNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                switch (state) {
 | 
				
			||||||
 | 
					                    case TelephonyManager.CALL_STATE_IDLE: // 待机,即无电话时,挂断时触发
 | 
				
			||||||
 | 
					                        dismiss();
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    case TelephonyManager.CALL_STATE_RINGING: // 响铃,来电时触发
 | 
				
			||||||
 | 
					                        isCallingIn = true;
 | 
				
			||||||
 | 
					                        updateUI();
 | 
				
			||||||
 | 
					                        show();
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    case TelephonyManager.CALL_STATE_OFFHOOK: // 摘机,接听或拨出电话时触发
 | 
				
			||||||
 | 
					                        updateUI();
 | 
				
			||||||
 | 
					                        show();
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 设置来电监听器
 | 
				
			||||||
 | 
					        telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
 | 
				
			||||||
 | 
					        if (telephonyManager != null) {
 | 
				
			||||||
 | 
					            telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initPhoneCallView() {
 | 
				
			||||||
 | 
					        windowManager = (WindowManager) getApplicationContext()
 | 
				
			||||||
 | 
					            .getSystemService(Context.WINDOW_SERVICE);
 | 
				
			||||||
 | 
					        int width = windowManager.getDefaultDisplay().getWidth();
 | 
				
			||||||
 | 
					        int height = windowManager.getDefaultDisplay().getHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        params = new WindowManager.LayoutParams();
 | 
				
			||||||
 | 
					        params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
 | 
				
			||||||
 | 
					        params.width = width;
 | 
				
			||||||
 | 
					        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
 | 
				
			||||||
 | 
					        params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 设置图片格式,效果为背景透明
 | 
				
			||||||
 | 
					        params.format = PixelFormat.TRANSLUCENT;
 | 
				
			||||||
 | 
					        // 设置 Window flag 为系统级弹框 | 覆盖表层
 | 
				
			||||||
 | 
					        params.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
 | 
				
			||||||
 | 
					            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
 | 
				
			||||||
 | 
					            WindowManager.LayoutParams.TYPE_PHONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 不可聚集(不响应返回键)| 全屏
 | 
				
			||||||
 | 
					        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
 | 
				
			||||||
 | 
					            | WindowManager.LayoutParams.FLAG_FULLSCREEN
 | 
				
			||||||
 | 
					            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 | 
				
			||||||
 | 
					        // API 19 以上则还可以开启透明状态栏与导航栏
 | 
				
			||||||
 | 
					        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 | 
				
			||||||
 | 
					            params.flags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
 | 
				
			||||||
 | 
					                | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
 | 
				
			||||||
 | 
					                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
 | 
				
			||||||
 | 
					                | WindowManager.LayoutParams.FLAG_FULLSCREEN
 | 
				
			||||||
 | 
					                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        FrameLayout interceptorLayout = new FrameLayout(this) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public boolean dispatchKeyEvent(KeyEvent event) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (event.getAction() == KeyEvent.ACTION_DOWN) {
 | 
				
			||||||
 | 
					                    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return super.dispatchKeyEvent(event);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        phoneCallView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE))
 | 
				
			||||||
 | 
					            .inflate(R.layout.view_phone_call, interceptorLayout);
 | 
				
			||||||
 | 
					        tvCallNumber = phoneCallView.findViewById(R.id.tv_call_number);
 | 
				
			||||||
 | 
					        btnOpenApp = phoneCallView.findViewById(R.id.btn_open_app);
 | 
				
			||||||
 | 
					        btnOpenApp.setOnClickListener(new View.OnClickListener(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onClick(View view) {
 | 
				
			||||||
 | 
					                    Intent intent = new Intent(getApplicationContext(), MainActivity.class);
 | 
				
			||||||
 | 
					                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
				
			||||||
 | 
					                    CallListenerService.this.startActivity(intent);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 显示顶级弹框展示通话信息
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void show() {
 | 
				
			||||||
 | 
					        if (!hasShown) {
 | 
				
			||||||
 | 
					            windowManager.addView(phoneCallView, params);
 | 
				
			||||||
 | 
					            hasShown = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 取消显示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void dismiss() {
 | 
				
			||||||
 | 
					        if (hasShown) {
 | 
				
			||||||
 | 
					            windowManager.removeView(phoneCallView);
 | 
				
			||||||
 | 
					            isCallingIn = false;
 | 
				
			||||||
 | 
					            hasShown = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updateUI() {
 | 
				
			||||||
 | 
					        tvCallNumber.setText(formatPhoneNumber(callNumber));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int callTypeDrawable = isCallingIn ? R.drawable.ic_phone_call_in : R.drawable.ic_phone_call_out;
 | 
				
			||||||
 | 
					        tvCallNumber.setCompoundDrawablesWithIntrinsicBounds(null, null,
 | 
				
			||||||
 | 
					                                                             getResources().getDrawable(callTypeDrawable), null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String formatPhoneNumber(String phoneNum) {
 | 
				
			||||||
 | 
					        if (!TextUtils.isEmpty(phoneNum) && phoneNum.length() == 11) {
 | 
				
			||||||
 | 
					            return phoneNum.substring(0, 3) + "-"
 | 
				
			||||||
 | 
					                + phoneNum.substring(3, 7) + "-"
 | 
				
			||||||
 | 
					                + phoneNum.substring(7);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return phoneNum;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.phonecallui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static cc.winboll.studio.contacts.listenphonecall.CallListenerService.formatPhoneNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.annotation.SuppressLint;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.view.WindowManager;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.RequiresApi;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.ActivityStack;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Timer;
 | 
				
			||||||
 | 
					import java.util.TimerTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 提供接打电话的界面,仅支持 Android M (6.0, API 23) 及以上的系统
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author aJIEw
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@RequiresApi(api = Build.VERSION_CODES.M)
 | 
				
			||||||
 | 
					public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TextView tvCallNumberLabel;
 | 
				
			||||||
 | 
					    private TextView tvCallNumber;
 | 
				
			||||||
 | 
					    private TextView tvPickUp;
 | 
				
			||||||
 | 
					    private TextView tvCallingTime;
 | 
				
			||||||
 | 
					    private TextView tvHangUp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private PhoneCallManager phoneCallManager;
 | 
				
			||||||
 | 
					    private PhoneCallService.CallType callType;
 | 
				
			||||||
 | 
					    private String phoneNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Timer onGoingCallTimer;
 | 
				
			||||||
 | 
					    private int callingTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void actionStart(Context context, String phoneNumber,
 | 
				
			||||||
 | 
					                                   PhoneCallService.CallType callType) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(context, PhoneCallActivity.class);
 | 
				
			||||||
 | 
					        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
				
			||||||
 | 
					        intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);
 | 
				
			||||||
 | 
					        intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);
 | 
				
			||||||
 | 
					        context.startActivity(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_phone_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ActivityStack.getInstance().addActivity(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        initView();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initData() {
 | 
				
			||||||
 | 
					        phoneCallManager = new PhoneCallManager(this);
 | 
				
			||||||
 | 
					        onGoingCallTimer = new Timer();
 | 
				
			||||||
 | 
					        if (getIntent() != null) {
 | 
				
			||||||
 | 
					            phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);
 | 
				
			||||||
 | 
					            callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initView() {
 | 
				
			||||||
 | 
					        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
 | 
				
			||||||
 | 
					                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar
 | 
				
			||||||
 | 
					                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
 | 
				
			||||||
 | 
					                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
 | 
				
			||||||
 | 
					        getWindow().getDecorView().setSystemUiVisibility(uiOptions);
 | 
				
			||||||
 | 
					        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tvCallNumberLabel = findViewById(R.id.tv_call_number_label);
 | 
				
			||||||
 | 
					        tvCallNumber = findViewById(R.id.tv_call_number);
 | 
				
			||||||
 | 
					        tvPickUp = findViewById(R.id.tv_phone_pick_up);
 | 
				
			||||||
 | 
					        tvCallingTime = findViewById(R.id.tv_phone_calling_time);
 | 
				
			||||||
 | 
					        tvHangUp = findViewById(R.id.tv_phone_hang_up);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tvCallNumber.setText(formatPhoneNumber(phoneNumber));
 | 
				
			||||||
 | 
					        tvPickUp.setOnClickListener(this);
 | 
				
			||||||
 | 
					        tvHangUp.setOnClickListener(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 打进的电话
 | 
				
			||||||
 | 
					        if (callType == PhoneCallService.CallType.CALL_IN) {
 | 
				
			||||||
 | 
					            tvCallNumberLabel.setText("来电号码");
 | 
				
			||||||
 | 
					            tvPickUp.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // 打出的电话
 | 
				
			||||||
 | 
					        else if (callType == PhoneCallService.CallType.CALL_OUT) {
 | 
				
			||||||
 | 
					            tvCallNumberLabel.setText("呼叫号码");
 | 
				
			||||||
 | 
					            tvPickUp.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            phoneCallManager.openSpeaker();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        showOnLockScreen();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void showOnLockScreen() {
 | 
				
			||||||
 | 
					        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
 | 
				
			||||||
 | 
					                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_FULLSCREEN |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
 | 
				
			||||||
 | 
					                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onClick(View v) {
 | 
				
			||||||
 | 
					        if (v.getId() == R.id.tv_phone_pick_up) {
 | 
				
			||||||
 | 
					            phoneCallManager.answer();
 | 
				
			||||||
 | 
					            tvPickUp.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					            tvCallingTime.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					            onGoingCallTimer.schedule(new TimerTask() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void run() {
 | 
				
			||||||
 | 
					                    runOnUiThread(new Runnable() {
 | 
				
			||||||
 | 
					                        @SuppressLint("SetTextI18n")
 | 
				
			||||||
 | 
					                        @Override
 | 
				
			||||||
 | 
					                        public void run() {
 | 
				
			||||||
 | 
					                            callingTime++;
 | 
				
			||||||
 | 
					                            tvCallingTime.setText("通话中:" + getCallingTime());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, 0, 1000);
 | 
				
			||||||
 | 
					        } else if (v.getId() == R.id.tv_phone_hang_up) {
 | 
				
			||||||
 | 
					            phoneCallManager.disconnect();
 | 
				
			||||||
 | 
					            stopTimer();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String getCallingTime() {
 | 
				
			||||||
 | 
					        int minute = callingTime / 60;
 | 
				
			||||||
 | 
					        int second = callingTime % 60;
 | 
				
			||||||
 | 
					        return (minute < 10 ? "0" + minute : minute) +
 | 
				
			||||||
 | 
					                ":" +
 | 
				
			||||||
 | 
					                (second < 10 ? "0" + second : second);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void stopTimer() {
 | 
				
			||||||
 | 
					        if (onGoingCallTimer != null) {
 | 
				
			||||||
 | 
					            onGoingCallTimer.cancel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        callingTime = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        phoneCallManager.destroy();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.phonecallui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.media.AudioManager;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					import android.telecom.Call;
 | 
				
			||||||
 | 
					import android.telecom.VideoProfile;
 | 
				
			||||||
 | 
					import androidx.annotation.RequiresApi;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.dun.Rules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RequiresApi(api = Build.VERSION_CODES.M)
 | 
				
			||||||
 | 
					public class PhoneCallManager {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Call call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Context context;
 | 
				
			||||||
 | 
					    private AudioManager audioManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PhoneCallManager(Context context) {
 | 
				
			||||||
 | 
					        this.context = context;
 | 
				
			||||||
 | 
					        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 接听电话
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void answer() {
 | 
				
			||||||
 | 
					        if (call != null) {
 | 
				
			||||||
 | 
					            call.answer(VideoProfile.STATE_AUDIO_ONLY);
 | 
				
			||||||
 | 
					            openSpeaker();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 断开电话,包括来电时的拒接以及接听后的挂断
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void disconnect() {
 | 
				
			||||||
 | 
					        if (call != null) {
 | 
				
			||||||
 | 
					            call.disconnect();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 打开免提
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void openSpeaker() {
 | 
				
			||||||
 | 
					        if (audioManager != null) {
 | 
				
			||||||
 | 
					            audioManager.setMode(AudioManager.MODE_IN_CALL);
 | 
				
			||||||
 | 
					            audioManager.setSpeakerphoneOn(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 销毁资源
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void destroy() {
 | 
				
			||||||
 | 
					        call = null;
 | 
				
			||||||
 | 
					        context = null;
 | 
				
			||||||
 | 
					        audioManager = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.phonecallui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 监听电话通信状态的服务,实现该类的同时必须提供电话管理的 UI
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author aJIEw
 | 
				
			||||||
 | 
					 * @see PhoneCallActivity
 | 
				
			||||||
 | 
					 * @see android.telecom.InCallService
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.media.AudioManager;
 | 
				
			||||||
 | 
					import android.os.Build;
 | 
				
			||||||
 | 
					import android.telecom.Call;
 | 
				
			||||||
 | 
					import android.telecom.InCallService;
 | 
				
			||||||
 | 
					import androidx.annotation.RequiresApi;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.ActivityStack;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.beans.RingTongBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.dun.Rules;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@RequiresApi(api = Build.VERSION_CODES.M)
 | 
				
			||||||
 | 
					public class PhoneCallService extends InCallService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "PhoneCallService";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private volatile int originalRingVolume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Call.Callback callback = new Call.Callback() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onStateChanged(Call call, int state) {
 | 
				
			||||||
 | 
					            super.onStateChanged(call, state);
 | 
				
			||||||
 | 
					            switch (state) {
 | 
				
			||||||
 | 
					                case Call.STATE_ACTIVE: {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                case Call.STATE_DISCONNECTED: {
 | 
				
			||||||
 | 
					                        ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCallAdded(Call call) {
 | 
				
			||||||
 | 
					        super.onCallAdded(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        call.registerCallback(callback);
 | 
				
			||||||
 | 
					        PhoneCallManager.call = call;
 | 
				
			||||||
 | 
					        CallType callType = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (call.getState() == Call.STATE_RINGING) {
 | 
				
			||||||
 | 
					            callType = CallType.CALL_IN;
 | 
				
			||||||
 | 
					        } else if (call.getState() == Call.STATE_CONNECTING) {
 | 
				
			||||||
 | 
					            callType = CallType.CALL_OUT;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (callType != null) {
 | 
				
			||||||
 | 
					            Call.Details details = call.getDetails();
 | 
				
			||||||
 | 
					            String phoneNumber = details.getHandle().getSchemeSpecificPart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 记录原始铃声音量
 | 
				
			||||||
 | 
					            AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
 | 
				
			||||||
 | 
					            originalRingVolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
 | 
				
			||||||
 | 
					            // 检查电话接收规则
 | 
				
			||||||
 | 
					            if (!Rules.getInstance(this).isAllowed(phoneNumber)) {
 | 
				
			||||||
 | 
					                // 预先静音
 | 
				
			||||||
 | 
					                audioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
 | 
				
			||||||
 | 
					                // 断开电话
 | 
				
			||||||
 | 
					                call.disconnect();
 | 
				
			||||||
 | 
					                // 停顿1秒,预防第一声铃声响动
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    Thread.sleep(1000);
 | 
				
			||||||
 | 
					                } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                    LogUtils.d(TAG, "");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // 恢复铃声音量
 | 
				
			||||||
 | 
					                audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
 | 
				
			||||||
 | 
					                // 屏蔽电话结束
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // 正常接听电话
 | 
				
			||||||
 | 
					            PhoneCallActivity.actionStart(this, phoneNumber, callType);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCallRemoved(Call call) {
 | 
				
			||||||
 | 
					        super.onCallRemoved(call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        call.unregisterCallback(callback);
 | 
				
			||||||
 | 
					        PhoneCallManager.call = null;
 | 
				
			||||||
 | 
					        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
 | 
				
			||||||
 | 
					        // 恢复铃声音量
 | 
				
			||||||
 | 
					        audioManager.setStreamVolume(AudioManager.STREAM_RING, originalRingVolume, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum CallType {
 | 
				
			||||||
 | 
					        CALL_IN,
 | 
				
			||||||
 | 
					        CALL_OUT,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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 android.media.RingtoneManager;
 | 
				
			||||||
 | 
					import android.net.Uri;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.services.MainService;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					import java.lang.ref.WeakReference;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MainReceiver extends BroadcastReceiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "MainReceiver";
 | 
				
			||||||
 | 
					    public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
 | 
				
			||||||
 | 
					    WeakReference<MainService> mwrService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MainReceiver(MainService service) {
 | 
				
			||||||
 | 
					        mwrService = new WeakReference<MainService>(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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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.contacts.beans.MainServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,247 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.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 cc.winboll.studio.contacts.listenphonecall.CallListenerService;
 | 
				
			||||||
 | 
					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.contacts.beans.MainServiceBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.handlers.MainServiceHandler;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.receivers.MainReceiver;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.services.MainService;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.threads.MainServiceThread;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.widgets.APPStatusWidget;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.SOS;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.bean.APPSOSBean;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.dun.Rules;
 | 
				
			||||||
 | 
					import android.media.AudioManager;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Rules.getInstance(this);
 | 
				
			||||||
 | 
					            //Rules.getInstance(this).add("18888888888", true);
 | 
				
			||||||
 | 
					            //Rules.getInstance(this).add("16769764848", true);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            startPhoneCallListener();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    void startPhoneCallListener() {
 | 
				
			||||||
 | 
					        Intent callListener = new Intent(this, CallListenerService.class);
 | 
				
			||||||
 | 
					        startService(callListener);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.threads;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/14 03:46:44
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.handlers.MainServiceHandler;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.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<MainServiceHandler> mwrMainServiceHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MainServiceThread(Context context, MainServiceHandler handler) {
 | 
				
			||||||
 | 
					        mContext = context;
 | 
				
			||||||
 | 
					        mwrMainServiceHandler = new WeakReference<MainServiceHandler>(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");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.widgets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/17 14:49:31
 | 
				
			||||||
 | 
					 * @Describe APPStatusWidget
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.app.PendingIntent;
 | 
				
			||||||
 | 
					import android.appwidget.AppWidgetManager;
 | 
				
			||||||
 | 
					import android.appwidget.AppWidgetProvider;
 | 
				
			||||||
 | 
					import android.content.ComponentName;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.widget.RemoteViews;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.R;
 | 
				
			||||||
 | 
					import cc.winboll.studio.contacts.threads.MainServiceThread;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					import com.hjq.toast.ToastUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class APPStatusWidget extends AppWidgetProvider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "APPSOSReportWidget";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String ACTION_STATUS_ACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_ACTIVE";
 | 
				
			||||||
 | 
					    public static final String ACTION_STATUS_NOACTIVE = "cc.winboll.studio.contacts.widgets.APPStatusWidget.ACTION_STATUS_NOACTIVE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
 | 
				
			||||||
 | 
					        for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					            updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					        super.onReceive(context, intent);
 | 
				
			||||||
 | 
					        if (intent.getAction().equals(ACTION_STATUS_ACTIVE)) {
 | 
				
			||||||
 | 
					            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
 | 
				
			||||||
 | 
					            int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
 | 
				
			||||||
 | 
					            for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					                updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (intent.getAction().equals(ACTION_STATUS_NOACTIVE)) {
 | 
				
			||||||
 | 
					            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
 | 
				
			||||||
 | 
					            int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, APPStatusWidget.class));
 | 
				
			||||||
 | 
					            for (int appWidgetId : appWidgetIds) {
 | 
				
			||||||
 | 
					                updateAppWidget(context, appWidgetManager, appWidgetId);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
 | 
				
			||||||
 | 
					        LogUtils.d(TAG, "updateAppWidget(...)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
 | 
				
			||||||
 | 
					        //设置按钮点击事件
 | 
				
			||||||
 | 
					        Intent intentAppButton = new Intent(context, APPStatusWidgetClickListener.class);
 | 
				
			||||||
 | 
					        intentAppButton.setAction(APPStatusWidgetClickListener.ACTION_APPICON_CLICK);
 | 
				
			||||||
 | 
					        PendingIntent pendingIntentAppButton = PendingIntent.getBroadcast(context, 0, intentAppButton, PendingIntent.FLAG_UPDATE_CURRENT);
 | 
				
			||||||
 | 
					        views.setOnClickPendingIntent(R.id.widgetlayoutImageView1, pendingIntentAppButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//        boolean isActive = !MainServiceThread.isExist();
 | 
				
			||||||
 | 
					//        if (isActive) {
 | 
				
			||||||
 | 
					//            views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher);
 | 
				
			||||||
 | 
					//        } else {
 | 
				
			||||||
 | 
					//            views.setImageViewResource(R.id.widgetlayoutImageView1, R.drawable.ic_launcher_disable);
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//        }
 | 
				
			||||||
 | 
					        appWidgetManager.updateAppWidget(appWidgetId, views);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void onAPPStatusWidgetClick(Context context) {
 | 
				
			||||||
 | 
					        ToastUtils.show("onAPPStatusWidgetClick");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					package cc.winboll.studio.contacts.widgets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @Author ZhanGSKen@AliYun.Com
 | 
				
			||||||
 | 
					 * @Date 2025/02/17 14:59:55
 | 
				
			||||||
 | 
					 * @Describe WidgetButtonClickListener
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import android.content.BroadcastReceiver;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					import cc.winboll.studio.libappbase.LogUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class APPStatusWidgetClickListener extends BroadcastReceiver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "APPStatusWidgetClickListener";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String ACTION_APPICON_CLICK = "cc.winboll.studio.contacts.widgets.APPStatusWidgetClickListener.ACTION_APPICON_CLICK";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					        String action = intent.getAction();
 | 
				
			||||||
 | 
					        if (action == null) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, String.format("action %s", action));
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (action.equals(ACTION_APPICON_CLICK)) {
 | 
				
			||||||
 | 
					            LogUtils.d(TAG, "ACTION_APPICON_CLICK");
 | 
				
			||||||
 | 
					            Toast.makeText(context, "ACTION_APPICON_CLICK", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								contacts/src/main/res/drawable/ic_launcher.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contacts/src/main/res/drawable/ic_launcher.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:clickable="true">
 | 
				
			||||||
 | 
					    <item android:drawable="@drawable/ic_launcher_background"/> 
 | 
				
			||||||
 | 
					    <item 
 | 
				
			||||||
 | 
					        android:left="15dp" 
 | 
				
			||||||
 | 
					        android:top="15dp" 
 | 
				
			||||||
 | 
					        android:right="15dp" 
 | 
				
			||||||
 | 
					        android:bottom="15dp"
 | 
				
			||||||
 | 
					        android:drawable="@drawable/ic_launcher_foreground"/>
 | 
				
			||||||
 | 
					</layer-list>
 | 
				
			||||||
							
								
								
									
										170
									
								
								contacts/src/main/res/drawable/ic_launcher_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								contacts/src/main/res/drawable/ic_launcher_background.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,170 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="108dp"
 | 
				
			||||||
 | 
					    android:height="108dp"
 | 
				
			||||||
 | 
					    android:viewportWidth="108"
 | 
				
			||||||
 | 
					    android:viewportHeight="108">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="@color/colorPrimary"
 | 
				
			||||||
 | 
					        android:pathData="M0,0h108v108h-108z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M9,0L9,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,0L19,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M29,0L29,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M39,0L39,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M49,0L49,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M59,0L59,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M69,0L69,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M79,0L79,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M89,0L89,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M99,0L99,108"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,9L108,9"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,19L108,19"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,29L108,29"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,39L108,39"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,49L108,49"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,59L108,59"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,69L108,69"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,79L108,79"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,89L108,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M0,99L108,99"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,29L89,29"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,39L89,39"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,49L89,49"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,59L89,59"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,69L89,69"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M19,79L89,79"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M29,19L29,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M39,19L39,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M49,19L49,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M59,19L59,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M69,19L69,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#00000000"
 | 
				
			||||||
 | 
					        android:pathData="M79,19L79,89"
 | 
				
			||||||
 | 
					        android:strokeWidth="0.8"
 | 
				
			||||||
 | 
					        android:strokeColor="#33FFFFFF" />
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										11
									
								
								contacts/src/main/res/drawable/ic_launcher_disable.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contacts/src/main/res/drawable/ic_launcher_disable.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:clickable="true">
 | 
				
			||||||
 | 
					    <item android:drawable="@drawable/ic_launcher_background"/> 
 | 
				
			||||||
 | 
					    <item 
 | 
				
			||||||
 | 
					        android:left="15dp" 
 | 
				
			||||||
 | 
					        android:top="15dp" 
 | 
				
			||||||
 | 
					        android:right="15dp" 
 | 
				
			||||||
 | 
					        android:bottom="15dp"
 | 
				
			||||||
 | 
					        android:drawable="@drawable/ic_launcher_foreground_disable"/>
 | 
				
			||||||
 | 
					</layer-list>
 | 
				
			||||||
							
								
								
									
										10
									
								
								contacts/src/main/res/drawable/ic_launcher_foreground.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								contacts/src/main/res/drawable/ic_launcher_foreground.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="24dp"
 | 
				
			||||||
 | 
					    android:height="24dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="24"
 | 
				
			||||||
 | 
					    android:viewportWidth="24">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?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="#FFAAAAAA"
 | 
				
			||||||
 | 
					        android:pathData="M16.61,15.15C16.15,15.15 15.77,14.78 15.77,14.32S16.15,13.5 16.61,13.5H16.61C17.07,13.5 17.45,13.86 17.45,14.32C17.45,14.78 17.07,15.15 16.61,15.15M7.41,15.15C6.95,15.15 6.57,14.78 6.57,14.32C6.57,13.86 6.95,13.5 7.41,13.5H7.41C7.87,13.5 8.24,13.86 8.24,14.32C8.24,14.78 7.87,15.15 7.41,15.15M16.91,10.14L18.58,7.26C18.67,7.09 18.61,6.88 18.45,6.79C18.28,6.69 18.07,6.75 18,6.92L16.29,9.83C14.95,9.22 13.5,8.9 12,8.91C10.47,8.91 9,9.24 7.73,9.82L6.04,6.91C5.95,6.74 5.74,6.68 5.57,6.78C5.4,6.87 5.35,7.08 5.44,7.25L7.1,10.13C4.25,11.69 2.29,14.58 2,18H22C21.72,14.59 19.77,11.7 16.91,10.14H16.91Z"/>
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_call_in.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_call_in.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="36dp"
 | 
				
			||||||
 | 
					    android:height="36dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="1024"
 | 
				
			||||||
 | 
					    android:viewportWidth="1024">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#fff"
 | 
				
			||||||
 | 
					        android:pathData="M847.53,307.03L597.5,307.03c-8.54,0.17 -13.66,-11.26 -13.66,-19.97 0.17,-8.7 5.12,-14.17 13.66,-14.17h250.02c30.21,-0.34 31.41,34.14 0,34.14zM736.6,428.71c-6.14,6.32 -16.05,6.32 -22.02,0.17l-128.34,-130.56c-5.98,-6.14 -5.8,-16.22 0.17,-22.53l122.37,-124.76c6.14,-6.31 16.05,-6.31 22.02,-0.34 5.98,6.14 5.81,16.22 -0.34,22.36L623.1,289.62l113.5,116.22a16.33,16.33 0,0 1,0 22.87zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.58,-95.23 -101.04,-112.47l-16.72,-23.38L345.09,418.82c13.99,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.27,324.44 303.27,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.8,130.73 251.22,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.73,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 3.92,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.31,11.61 -26.62,46.59 -36.69,63.83 10.92,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.02,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_call_out.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_call_out.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="36dp"
 | 
				
			||||||
 | 
					    android:height="36dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="1024"
 | 
				
			||||||
 | 
					    android:viewportWidth="1024">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#fff"
 | 
				
			||||||
 | 
					        android:pathData="M861.86,307.03L614.4,307.03c-31.06,-0.17 -29.86,-34.48 0,-34.14h247.46c8.54,0.17 13.31,5.46 13.49,14.17 0,8.87 -4.95,20.14 -13.49,19.97zM872.96,298.5l-126.98,130.56c-5.98,6.14 -15.7,5.98 -21.85,-0.17 -6.14,-6.32 -6.14,-16.38 -0.17,-22.36l112.3,-116.22L730.11,173.74c-6.14,-6.31 -6.14,-16.38 -0.17,-22.35 5.97,-6.14 15.7,-5.98 21.84,0.34L872.96,276.48c5.8,5.8 5.98,15.87 0,22.02zM833.02,717.99c-18.78,-12.63 -122.37,-64.85 -141.99,-77.14 -2.05,0 -7.34,0.68 -14.85,4.43 -8.02,4.1 -31.23,16.9 -63.66,34.99l-25.26,14.16 -23.9,-16.38c-17.75,-12.11 -56.32,-41.3 -115.03,-98.64 -58.88,-57.69 -88.41,-95.23 -100.86,-112.47L330.75,443.56l14.5,-24.74c14,-23.9 30.72,-52.9 35.84,-62.46 4.1,-7.51 4.44,-12.63 4.44,-14.17v-0.51C374.44,324.44 303.45,219.65 290.3,201.22c-1.88,-0.34 -5.8,0 -10.75,3.42 -40.28,28.5 -82.43,68.95 -88.41,84.65C195.24,373.42 270.85,491.52 404.48,622.25c133.63,130.73 251.05,205.49 336.9,209.41 16.22,-5.63 62.81,-52.05 91.65,-91.14 2.05,-2.9 3.24,-3.58 3.24,-6.99 0,-0.51 -3.07,-15.02 -3.24,-15.53zM867.5,770.73c-0.68,1.02 -81.58,108.03 -126.12,106.67 -125.1,-3.93 -265.56,-128.86 -366.94,-227.84 -101.38,-98.99 -225.45,-238.25 -229.38,-360.96v-1.02c0,-43.01 124.76,-128 125.78,-128.69 19.62,-13.14 36.35,-13.82 52.39,10.41 7.85,10.92 87.73,123.9 96.94,138.58 4.1,6.49 11.09,25.94 11.09,34.48 0,10.92 -3.24,23.21 -9.73,34.98 -6.14,11.61 -26.62,46.59 -36.69,63.83 10.75,15.19 46.76,61.44 103.94,117.25 56.83,55.63 85.16,72.19 100.69,82.77 17.58,-9.73 53.42,-29.7 65.2,-35.67 22.18,-11.44 44.37,-12.8 59.39,-3.42 14.16,8.7 136.19,88.41 147.79,96.6 10.93,7.68 16.9,21.67 16.9,36.69 0.17,11.78 -3.58,24.41 -11.26,35.33z" />
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_hang_up.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								contacts/src/main/res/drawable/ic_phone_hang_up.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="64dp"
 | 
				
			||||||
 | 
					    android:height="64dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="1024"
 | 
				
			||||||
 | 
					    android:viewportWidth="1024">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#201f1f"
 | 
				
			||||||
 | 
					        android:pathData="M841.22,856.06c185.86,-185.86 185.86,-487.42 0,-673.79C655.36,-3.58 353.79,-3.58 167.42,182.27c-185.86,185.86 -185.86,487.42 0,673.79 186.37,185.86 487.94,185.86 673.79,0zM218.62,495.1c0,-47.62 111.1,-113.66 285.7,-113.66 175.1,0 285.7,66.05 285.7,113.66 0,40.96 10.75,102.4 -73.73,93.18 -84.48,-9.22 -78.85,-40.96 -78.85,-83.46 0,-29.7 -68.61,-36.35 -133.12,-36.35 -65.02,0 -133.12,6.66 -133.12,36.35 0,42.5 5.63,74.24 -78.85,83.46 -84.99,9.22 -73.73,-51.71 -73.73,-93.18z" />
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										30
									
								
								contacts/src/main/res/drawable/ic_phone_pick_up.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								contacts/src/main/res/drawable/ic_phone_pick_up.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:width="64dp"
 | 
				
			||||||
 | 
					    android:height="64dp"
 | 
				
			||||||
 | 
					    android:viewportHeight="1024"
 | 
				
			||||||
 | 
					    android:viewportWidth="1024">
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M277.9,742s167.9,-294.1 465.3,-465.3C575.7,577.8 277.9,742 277.9,742z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M743.1,742S575.2,447.9 277.8,276.7C445.3,577.8 743.1,742 743.1,742z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M284.1,343m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M284.1,515.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M284.1,683.5m-49,0a49,49 0,1 0,98 0,49 49,0 1,0 -98,0Z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M808.4,341.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,516.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0zM808.4,684.6s-208.5,-61.2 -420,0c211.5,64.8 420,0 420,0z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#0EC469"
 | 
				
			||||||
 | 
					        android:pathData="M512,512m-448,0a448,448 0,1 0,896 0,448 448,0 1,0 -896,0Z" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					        android:fillColor="#FFFFFF"
 | 
				
			||||||
 | 
					        android:pathData="M701.4,786.8h-0.3c-3.2,-0.3 -6.4,-0.8 -9.5,-1.4C290,725.2 237.2,331.5 237.2,331.5h0.1c-5.9,-27.8 0.8,-56.8 22.5,-78.5 9.5,-9.5 22.5,-18.1 31.3,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -8.5,8.5 -17.9,14.4 -28.4,18.6 57.7,214.5 272.1,271.5 272.1,271.5l0.3,0.2c4.2,-10.5 9.6,-19.3 18.1,-27.8 9.5,-9.5 22.6,-18.1 31.4,-19.2l111.6,111.6c-1.1,8.8 -9.6,21.7 -19.1,31.2 -18.9,19 -43.4,26.3 -68.2,24.1z" />
 | 
				
			||||||
 | 
					</vector>
 | 
				
			||||||
							
								
								
									
										10
									
								
								contacts/src/main/res/drawable/shape_gradient.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								contacts/src/main/res/drawable/shape_gradient.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <gradient
 | 
				
			||||||
 | 
					        android:angle="180"
 | 
				
			||||||
 | 
					        android:endColor="#FFFFFFFF"
 | 
				
			||||||
 | 
					        android:startColor="#FFFFFFFF"
 | 
				
			||||||
 | 
					        android:type="linear" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <corners android:radius="10dp" />
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
							
								
								
									
										28
									
								
								contacts/src/main/res/layout/activity_call.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								contacts/src/main/res/layout/activity_call.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <EditText
 | 
				
			||||||
 | 
					        android:id="@+id/phone_number"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:hint="请输入电话号码"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/dial_button"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:text="拨号"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:id="@+id/call_status"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:paddingTop="16dp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										22
									
								
								contacts/src/main/res/layout/activity_dialer.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contacts/src/main/res/layout/activity_dialer.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <EditText
 | 
				
			||||||
 | 
					        android:id="@+id/phone_number_edit_text"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:hint="Enter phone number" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/dial_button"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:text="Dial" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										27
									
								
								contacts/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								contacts/src/main/res/layout/activity_main.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
						xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
						android:orientation="vertical"
 | 
				
			||||||
 | 
						android:layout_width="match_parent"
 | 
				
			||||||
 | 
						android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<androidx.appcompat.widget.Toolbar
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="wrap_content"
 | 
				
			||||||
 | 
							android:id="@+id/activitymainToolbar1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<androidx.viewpager.widget.ViewPager
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="0dp"
 | 
				
			||||||
 | 
							android:layout_weight="1.0"
 | 
				
			||||||
 | 
							android:id="@+id/activitymainViewPager1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<com.google.android.material.tabs.TabLayout
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="60dp"
 | 
				
			||||||
 | 
							android:id="@+id/activitymainTabLayout1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										95
									
								
								contacts/src/main/res/layout/activity_phone_call.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								contacts/src/main/res/layout/activity_phone_call.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    tools:context=".phonecallui.PhoneCallActivity">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <RelativeLayout
 | 
				
			||||||
 | 
					        android:id="@+id/rl_user_info"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="300dp"
 | 
				
			||||||
 | 
					        android:background="@color/colorPrimaryDark">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/tv_call_number_label"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_above="@+id/tv_call_number"
 | 
				
			||||||
 | 
					            android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					            android:gravity="center"
 | 
				
			||||||
 | 
					            android:text="来电号码"
 | 
				
			||||||
 | 
					            android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					            android:textSize="18sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/tv_call_number"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:gravity="center"
 | 
				
			||||||
 | 
					            android:textAlignment="center"
 | 
				
			||||||
 | 
					            android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					            android:textSize="28sp"
 | 
				
			||||||
 | 
					            android:textStyle="bold"
 | 
				
			||||||
 | 
					            android:layout_centerInParent="true"
 | 
				
			||||||
 | 
					            tools:text="133-9527-9527" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </RelativeLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <RelativeLayout
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
 | 
					        android:background="@android:color/white">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/tv_phone_calling_time"
 | 
				
			||||||
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_centerHorizontal="true"
 | 
				
			||||||
 | 
					            android:layout_marginTop="24dp"
 | 
				
			||||||
 | 
					            android:text="通话中:01:33"
 | 
				
			||||||
 | 
					            android:textColor="@android:color/black"
 | 
				
			||||||
 | 
					            android:textSize="18sp"
 | 
				
			||||||
 | 
					            android:visibility="gone"
 | 
				
			||||||
 | 
					            tools:visibility="visible" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <RelativeLayout
 | 
				
			||||||
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_centerInParent="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/tv_phone_hang_up"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:drawablePadding="16dp"
 | 
				
			||||||
 | 
					                android:drawableTop="@drawable/ic_phone_hang_up"
 | 
				
			||||||
 | 
					                android:foreground="?android:attr/selectableItemBackground"
 | 
				
			||||||
 | 
					                android:gravity="center"
 | 
				
			||||||
 | 
					                android:padding="8dp"
 | 
				
			||||||
 | 
					                android:text="挂  断"
 | 
				
			||||||
 | 
					                android:textColor="@android:color/black"
 | 
				
			||||||
 | 
					                tools:visibility="visible" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/tv_phone_pick_up"
 | 
				
			||||||
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_marginLeft="50dp"
 | 
				
			||||||
 | 
					                android:layout_toRightOf="@id/tv_phone_hang_up"
 | 
				
			||||||
 | 
					                android:drawablePadding="16dp"
 | 
				
			||||||
 | 
					                android:drawableTop="@drawable/ic_phone_pick_up"
 | 
				
			||||||
 | 
					                android:foreground="?android:attr/selectableItemBackground"
 | 
				
			||||||
 | 
					                android:gravity="center"
 | 
				
			||||||
 | 
					                android:padding="8dp"
 | 
				
			||||||
 | 
					                android:text="接  听"
 | 
				
			||||||
 | 
					                android:textColor="@android:color/black"
 | 
				
			||||||
 | 
					                android:visibility="gone"
 | 
				
			||||||
 | 
					                tools:visibility="visible" />
 | 
				
			||||||
 | 
					        </RelativeLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </RelativeLayout>
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										51
									
								
								contacts/src/main/res/layout/activity_settings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								contacts/src/main/res/layout/activity_settings.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
						xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
						android:orientation="vertical"
 | 
				
			||||||
 | 
						android:layout_width="match_parent"
 | 
				
			||||||
 | 
						android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<androidx.appcompat.widget.Toolbar
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="wrap_content"
 | 
				
			||||||
 | 
							android:id="@+id/activitymainToolbar1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<LinearLayout
 | 
				
			||||||
 | 
							android:orientation="vertical"
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="0dp"
 | 
				
			||||||
 | 
							android:layout_weight="1.0">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<TextView
 | 
				
			||||||
 | 
								android:layout_width="wrap_content"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:text="应用权限设置:"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<LinearLayout
 | 
				
			||||||
 | 
								android:orientation="horizontal"
 | 
				
			||||||
 | 
								android:layout_width="match_parent"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:gravity="right">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<Button
 | 
				
			||||||
 | 
									android:layout_width="wrap_content"
 | 
				
			||||||
 | 
									android:layout_height="wrap_content"
 | 
				
			||||||
 | 
									android:text="悬浮窗设置"
 | 
				
			||||||
 | 
									android:id="@+id/activitysettingsButton2"
 | 
				
			||||||
 | 
									android:onClick="onCanDrawOverlays"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<Button
 | 
				
			||||||
 | 
									android:layout_width="wrap_content"
 | 
				
			||||||
 | 
									android:layout_height="wrap_content"
 | 
				
			||||||
 | 
									android:text="默认拨号设置"
 | 
				
			||||||
 | 
									android:id="@+id/activitysettingsButton1"
 | 
				
			||||||
 | 
									android:onClick="onDefaultPhone"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										29
									
								
								contacts/src/main/res/layout/fragment_call.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								contacts/src/main/res/layout/fragment_call.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
						android:orientation="vertical"
 | 
				
			||||||
 | 
						android:layout_width="match_parent"
 | 
				
			||||||
 | 
						android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<LinearLayout
 | 
				
			||||||
 | 
							android:orientation="horizontal"
 | 
				
			||||||
 | 
							android:layout_width="match_parent"
 | 
				
			||||||
 | 
							android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<EditText
 | 
				
			||||||
 | 
								android:layout_width="0dp"
 | 
				
			||||||
 | 
								android:ems="10"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:layout_weight="1.0"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<TextView
 | 
				
			||||||
 | 
								android:layout_width="wrap_content"
 | 
				
			||||||
 | 
								android:layout_height="wrap_content"
 | 
				
			||||||
 | 
								android:text="Text"
 | 
				
			||||||
 | 
								android:id="@+id/page_text"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								contacts/src/main/res/layout/fragment_contacts.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								contacts/src/main/res/layout/fragment_contacts.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:text="Text"
 | 
				
			||||||
 | 
					        android:id="@+id/page_text"/>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										12
									
								
								contacts/src/main/res/layout/fragment_log.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contacts/src/main/res/layout/fragment_log.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent">
 | 
				
			||||||
 | 
					     <cc.winboll.studio.libappbase.LogView
 | 
				
			||||||
 | 
					            android:layout_height="match_parent"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:id="@+id/logview"/>
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										33
									
								
								contacts/src/main/res/layout/view_phone_call.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								contacts/src/main/res/layout/view_phone_call.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:id="@+id/rl_user_info"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					    android:background="@color/colorPrimary">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <TextView
 | 
				
			||||||
 | 
					        android:id="@+id/tv_call_number"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_centerHorizontal="true"
 | 
				
			||||||
 | 
					        android:layout_marginTop="30dp"
 | 
				
			||||||
 | 
					        android:gravity="center"
 | 
				
			||||||
 | 
					        android:textAlignment="center"
 | 
				
			||||||
 | 
					        android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					        android:textSize="18sp"
 | 
				
			||||||
 | 
					        tools:text="133-8892-9579" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/btn_open_app"
 | 
				
			||||||
 | 
					        style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_below="@id/tv_call_number"
 | 
				
			||||||
 | 
					        android:gravity="center"
 | 
				
			||||||
 | 
					        android:text="打开 Phone Call App"
 | 
				
			||||||
 | 
					        android:textAlignment="center"
 | 
				
			||||||
 | 
					        android:textSize="16sp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</RelativeLayout>
 | 
				
			||||||
							
								
								
									
										32
									
								
								contacts/src/main/res/layout/view_toast.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								contacts/src/main/res/layout/view_toast.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
					    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					    android:background="@drawable/shape_gradient"
 | 
				
			||||||
 | 
					    android:gravity="center"
 | 
				
			||||||
 | 
					    android:orientation="vertical"
 | 
				
			||||||
 | 
					    android:padding="10dp">
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <LinearLayout
 | 
				
			||||||
 | 
					        android:orientation="horizontal"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <ImageView
 | 
				
			||||||
 | 
					            android:layout_width="40dp"
 | 
				
			||||||
 | 
					            android:layout_height="40dp"
 | 
				
			||||||
 | 
					            android:src="@drawable/ic_launcher"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@android:id/message"
 | 
				
			||||||
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_margin="10dp"
 | 
				
			||||||
 | 
					            android:textColor="#FF000000"
 | 
				
			||||||
 | 
					            android:textSize="16sp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
							
								
								
									
										15
									
								
								contacts/src/main/res/layout/widget_layout.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								contacts/src/main/res/layout/widget_layout.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<LinearLayout
 | 
				
			||||||
 | 
						xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
						android:orientation="vertical"
 | 
				
			||||||
 | 
						android:layout_width="40dp"
 | 
				
			||||||
 | 
						android:layout_height="40dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<ImageView
 | 
				
			||||||
 | 
							android:layout_width="40dp"
 | 
				
			||||||
 | 
							android:layout_height="40dp"
 | 
				
			||||||
 | 
							android:id="@+id/widgetlayoutImageView1"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								contacts/src/main/res/menu/toolbar_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contacts/src/main/res/menu/toolbar_main.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<menu xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
						xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <item
 | 
				
			||||||
 | 
					        android:id="@+id/item_call"
 | 
				
			||||||
 | 
					        android:title="CallActivity"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <item
 | 
				
			||||||
 | 
					        android:id="@+id/item_settings"
 | 
				
			||||||
 | 
					        android:title="SettingsActivity"/>
 | 
				
			||||||
 | 
					</menu>
 | 
				
			||||||
							
								
								
									
										7
									
								
								contacts/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								contacts/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					    <!-- WinBoll 默认方案 -->
 | 
				
			||||||
 | 
					    <color name="colorPrimary">#FF196ABC</color>
 | 
				
			||||||
 | 
					    <color name="colorPrimaryDark">#FF002B57</color>
 | 
				
			||||||
 | 
					    <color name="colorAccent">#FF80BFFF</color>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
							
								
								
									
										6
									
								
								contacts/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contacts/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <string name="app_name">Contacts</string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
							
								
								
									
										17
									
								
								contacts/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								contacts/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					    <style name="MyAppTheme" parent="APPBaseTheme">
 | 
				
			||||||
 | 
					        <item name="colorPrimary">@color/colorPrimary</item>
 | 
				
			||||||
 | 
					        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
 | 
				
			||||||
 | 
					        <item name="colorAccent">@color/colorAccent</item>
 | 
				
			||||||
 | 
					        <item name="android:windowFullscreen">true</item>
 | 
				
			||||||
 | 
					        <item name="android:windowContentOverlay">@null</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="GlobalCrashActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
 | 
				
			||||||
 | 
					        <item name="colorTittle">@color/colorAccent</item>
 | 
				
			||||||
 | 
					        <item name="colorTittleBackgound">@color/colorPrimary</item>
 | 
				
			||||||
 | 
					        <item name="colorText">@color/colorAccent</item>
 | 
				
			||||||
 | 
					        <item name="colorTextBackgound">@color/colorPrimaryDark</item>
 | 
				
			||||||
 | 
						</style>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
							
								
								
									
										8
									
								
								contacts/src/main/res/xml/appwidget_provider_info.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								contacts/src/main/res/xml/appwidget_provider_info.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:minWidth="40dp"
 | 
				
			||||||
 | 
					    android:minHeight="40dp"
 | 
				
			||||||
 | 
					    android:updatePeriodMillis="0"
 | 
				
			||||||
 | 
					    android:initialLayout="@layout/widget_layout"
 | 
				
			||||||
 | 
					    android:resizeMode="none">
 | 
				
			||||||
 | 
					</appwidget-provider>
 | 
				
			||||||
							
								
								
									
										25
									
								
								contacts/src/main/res/xml/studio_provider.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								contacts/src/main/res/xml/studio_provider.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<paths>
 | 
				
			||||||
 | 
					    <external-path
 | 
				
			||||||
 | 
					        name="external_storage_root"
 | 
				
			||||||
 | 
					        path="." />
 | 
				
			||||||
 | 
					    <files-path
 | 
				
			||||||
 | 
					        name="files_path"
 | 
				
			||||||
 | 
					        path="." />
 | 
				
			||||||
 | 
					    <cache-path
 | 
				
			||||||
 | 
					        name="cache_path"
 | 
				
			||||||
 | 
					        path="." />
 | 
				
			||||||
 | 
					    <!--/storage/emulated/0/Android/data/...-->
 | 
				
			||||||
 | 
					    <external-files-path
 | 
				
			||||||
 | 
					        name="external_file_path"
 | 
				
			||||||
 | 
					        path="." />
 | 
				
			||||||
 | 
					    <!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
 | 
				
			||||||
 | 
					    <external-cache-path
 | 
				
			||||||
 | 
					        name="external_cache_path"
 | 
				
			||||||
 | 
					        path="." />
 | 
				
			||||||
 | 
					    <!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有
 | 
				
			||||||
 | 
					    -->
 | 
				
			||||||
 | 
					    <root-path
 | 
				
			||||||
 | 
					        name="root_path"
 | 
				
			||||||
 | 
					        path="" />
 | 
				
			||||||
 | 
					</paths>
 | 
				
			||||||
							
								
								
									
										12
									
								
								contacts/src/stage/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								contacts/src/stage/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools" >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Put flavor specific code here -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</manifest>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								contacts/src/stage/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contacts/src/stage/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Put flavor specific strings here -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
 | 
					# http://www.gradle.org/docs/current/userguide/build_environment.html
 | 
				
			||||||
# Specifies the JVM arguments used for the daemon process.
 | 
					# Specifies the JVM arguments used for the daemon process.
 | 
				
			||||||
# The setting is particularly useful for tweaking memory settings.
 | 
					# The setting is particularly useful for tweaking memory settings.
 | 
				
			||||||
org.gradle.jvmargs=-Xmx2048m
 | 
					org.gradle.jvmargs=-Xmx4096m
 | 
				
			||||||
# When configured, Gradle will run in incubating parallel mode.
 | 
					# When configured, Gradle will run in incubating parallel mode.
 | 
				
			||||||
# This option should only be used with decoupled projects. More details, visit
 | 
					# This option should only be used with decoupled projects. More details, visit
 | 
				
			||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 | 
					# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 | 
				
			||||||
@@ -18,7 +18,9 @@ android.useAndroidX=true
 | 
				
			|||||||
# Automatically convert third-party libraries to use AndroidX
 | 
					# Automatically convert third-party libraries to use AndroidX
 | 
				
			||||||
android.enableJetifier=true
 | 
					android.enableJetifier=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#org.gradle.caching=true
 | 
					org.gradle.caching=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android.disableAutomaticComponentCreation=true
 | 
					android.disableAutomaticComponentCreation=true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					android.injected.testOnly=false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user