Commit c54c5db5 authored by YONG-LIN SU's avatar YONG-LIN SU

新增crash handle及 android 12兼容

parent 3fc1522b
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<option name="testRunner" value="GRADLE" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" /> <option name="gradleJvm" value="Embedded JDK" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
......
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DesignSurface"> <component name="DesignSurface">
<option name="filePathToZoomLevelMap"> <option name="filePathToZoomLevelMap">
...@@ -70,7 +71,7 @@ ...@@ -70,7 +71,7 @@
</map> </map>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">
......
...@@ -74,9 +74,13 @@ dependencies { ...@@ -74,9 +74,13 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.8.1' implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
annotationProcessor 'androidx.room:room-compiler:2.4.2' annotationProcessor 'androidx.room:room-compiler:2.4.2'
// log 採集器
implementation 'org.slf4j:slf4j-api:2.0.7'
implementation 'uk.uuid.slf4j:slf4j-android:2.0.7-0'
} }
\ No newline at end of file
package ecom.android.newparkapp;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
public class CrashHandlers implements Thread.UncaughtExceptionHandler {
public static final String TAG = "CrashHandler";
// 系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
// CrashHandler实例
private static CrashHandlers INSTANCE = new CrashHandlers();
// 程序的Context对象
private Context mContext;
// 用来存储设备信息和异常信息
private Map<String, String> infos = new HashMap<String, String>();
/**
* 保证只有一个CrashHandler实例
*/
private CrashHandlers() {
}
/**
* 获取CrashHandler实例 ,单例模式
*/
public static CrashHandlers getInstance() {
return INSTANCE;
}
/**
* 初始化
*
* @param context
*/
public void init(Context context) {
Log.e("CrashHandler","...........................2");
mContext = context;
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
}
// 退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
// 收集设备参数信息
collectDeviceInfo(mContext);
// 保存日志文件
saveCrashInfo2File(ex);
return false;
}
/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
Log.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
}
/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
Log.e(TAG,"------1"+ ex.getMessage());
StringBuffer sb = new StringBuffer();
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
sb.append("\r\n");
sb.append("\r\n");
sb.append("\r\n");
sb.append("************************************************"+time+"****************************************"+"\r\n");
sb.append("\r\n");
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(key + "=" + value + "\n");
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
try {
String fileName = "errorLog.txt";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory() + "/ParkData/errorLog";
// String path = mContext.getExternalCacheDir() + "/errorLog";
Log.e(TAG, "-----2"+path);
File dir = new File(path);
if (!dir.exists()) {
dir.mkdir();
}
// File file = new File(path + File.separator + fileName);
File file = new File(dir ,File.separator + System.currentTimeMillis()+fileName);
if (!file.exists()) {
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file, true);
fileWriter.write(sb.toString());
Log.e(TAG, "--write===="+path);
fileWriter.close();
}
return fileName;
} catch (Exception e) {
Log.e(TAG, "--====="+e.toString());
return null;
}
}
}
...@@ -3,6 +3,9 @@ package ecom.android.newparkapp.view; ...@@ -3,6 +3,9 @@ package ecom.android.newparkapp.view;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; import static android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
...@@ -55,6 +58,7 @@ public class MainActivity extends AppCompatActivity { ...@@ -55,6 +58,7 @@ public class MainActivity extends AppCompatActivity {
private ActivityResultLauncher requestAllFilesAccessPermissionLauncher; // Android 11 以上需申請較高權限 private ActivityResultLauncher requestAllFilesAccessPermissionLauncher; // Android 11 以上需申請較高權限
private String[] permissions = new String[]{ WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, private String[] permissions = new String[]{ WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE,
ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_LOCATION_EXTRA_COMMANDS}; ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_LOCATION_EXTRA_COMMANDS};
private String[] bluetoothPermissionsForAndroid12 = new String[]{BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE};
private MutableLiveData<Boolean> hasPermissions = new MutableLiveData<>(); private MutableLiveData<Boolean> hasPermissions = new MutableLiveData<>();
private ActivityResultLauncher settingActivityResultLauncher; private ActivityResultLauncher settingActivityResultLauncher;
...@@ -78,10 +82,14 @@ public class MainActivity extends AppCompatActivity { ...@@ -78,10 +82,14 @@ public class MainActivity extends AppCompatActivity {
// 註冊權限請求結果處理 // 註冊權限請求結果處理
resultLauncherRegister(); resultLauncherRegister();
// 檢查權限 // 確認其他權限
//permissionRequest(); permissionRequest();
// 兼容 Android 11 權限檢查 // 兼容 Android 11 權限檢查
permissionRequestAndroid11(); permissionRequestAndroid11();
// 兼容 Android 13 藍芽權限檢查
permissionRequestAndroid13();
// 再確一次結果
hasPermissions.setValue(checkPermissions());
// 監聽權限驗證變化 // 監聽權限驗證變化
// 有權限再綁定按鈕功能 // 有權限再綁定按鈕功能
...@@ -105,6 +113,8 @@ public class MainActivity extends AppCompatActivity { ...@@ -105,6 +113,8 @@ public class MainActivity extends AppCompatActivity {
// 複製assert下的基本資料,並匯入 // 複製assert下的基本資料,並匯入
t01ImportDbViewModel.DBDataPrepare(); t01ImportDbViewModel.DBDataPrepare();
} }
}else{
Toast.makeText(this, "取得權限失敗,請重新開啟程式後,重新嘗試", Toast.LENGTH_LONG).show();
} }
}); });
} }
...@@ -148,11 +158,8 @@ public class MainActivity extends AppCompatActivity { ...@@ -148,11 +158,8 @@ public class MainActivity extends AppCompatActivity {
*/ */
private void permissionRequest(){ private void permissionRequest(){
// 參考資料 https://developer.android.com/training/permissions/requesting // 參考資料 https://developer.android.com/training/permissions/requesting
hasPermissions.setValue(false);
if (!checkPermissions()){ if (!checkPermissions()){
requestPermissionLauncher.launch(permissions); requestPermissionLauncher.launch(permissions);
}else {
hasPermissions.setValue(true);
} }
} }
...@@ -164,21 +171,42 @@ public class MainActivity extends AppCompatActivity { ...@@ -164,21 +171,42 @@ public class MainActivity extends AppCompatActivity {
return; return;
} }
} }
permissionRequest(); //permissionRequest();
}
private void permissionRequestAndroid13(){
// 2022/8/3 Android 12 以上的版本 需額外請求藍芽權限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
requestPermissionLauncher.launch(bluetoothPermissionsForAndroid12);
return;
}
} }
/** /**
* @return 檢查所有權限 * @return 檢查所有權限
*/ */
private boolean checkPermissions(){ private boolean checkPermissions(){
boolean ret = true; // 一般權限請確認
for (String permission : permissions){ for (String permission : permissions){
if(ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ if(ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
ret = false; return false;
break;
} }
} }
return ret; // Android 11以上版本,權限確認
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!isExternalStorageManager()){
return false;
}
}
// Android 12 以上的版本 需額外請求藍芽權限確認
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
for (String permission : bluetoothPermissionsForAndroid12){
if(ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
return false;
}
}
}
return true;
} }
/** /**
...@@ -193,12 +221,12 @@ public class MainActivity extends AppCompatActivity { ...@@ -193,12 +221,12 @@ public class MainActivity extends AppCompatActivity {
return; return;
} }
} }
hasPermissions.setValue(true); //hasPermissions.setValue(true);
} }
); );
requestAllFilesAccessPermissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { requestAllFilesAccessPermissionLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
permissionRequest(); //permissionRequest();
}); });
settingActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { settingActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
...@@ -246,6 +274,12 @@ public class MainActivity extends AppCompatActivity { ...@@ -246,6 +274,12 @@ public class MainActivity extends AppCompatActivity {
} }
private void btnStartOnClicked(Shift shift){ private void btnStartOnClicked(Shift shift){
// 檢查是否取得相關權限
if (!checkPermissions()){
Toast.makeText(this, "取得權限失敗,請重新開啟程式後,重新嘗試", Toast.LENGTH_LONG).show();
return;
}
if (t01SettingViewModel.getCurrentUser() == null || t01SettingViewModel.getCurrentUser().id == 999){ if (t01SettingViewModel.getCurrentUser() == null || t01SettingViewModel.getCurrentUser().id == 999){
Toast.makeText(this,"請先設定使用者",Toast.LENGTH_LONG).show(); Toast.makeText(this,"請先設定使用者",Toast.LENGTH_LONG).show();
return; return;
......
...@@ -33,6 +33,9 @@ import androidx.databinding.DataBindingUtil; ...@@ -33,6 +33,9 @@ import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -42,6 +45,7 @@ import java.util.List; ...@@ -42,6 +45,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import ecom.android.newparkapp.Common; import ecom.android.newparkapp.Common;
import ecom.android.newparkapp.CrashHandlers;
import ecom.android.newparkapp.R; import ecom.android.newparkapp.R;
import ecom.android.newparkapp.databinding.ActivityT02StartBinding; import ecom.android.newparkapp.databinding.ActivityT02StartBinding;
import ecom.android.newparkapp.databinding.AlertDialogT02DeviceListBinding; import ecom.android.newparkapp.databinding.AlertDialogT02DeviceListBinding;
...@@ -113,6 +117,10 @@ public class T02StartActivity extends AppCompatActivity { ...@@ -113,6 +117,10 @@ public class T02StartActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// 初始化例外處理器
CrashHandlers.getInstance().init(this);
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_t02_start); dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_t02_start);
dataBinding.setLifecycleOwner(this); dataBinding.setLifecycleOwner(this);
......
...@@ -4,6 +4,7 @@ import static android.Manifest.permission.BLUETOOTH_ADVERTISE; ...@@ -4,6 +4,7 @@ import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN; import static android.Manifest.permission.BLUETOOTH_SCAN;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
...@@ -68,8 +69,8 @@ public class BlueToothPortViewModel extends AndroidViewModel { ...@@ -68,8 +69,8 @@ public class BlueToothPortViewModel extends AndroidViewModel {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限 // Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){ for (String permission : bluetoothPermissions) {
if(ActivityCompat.checkSelfPermission(application, permission) != PackageManager.PERMISSION_GRANTED){ if (ActivityCompat.checkSelfPermission(application, permission) != PackageManager.PERMISSION_GRANTED) {
return; return;
} }
} }
...@@ -107,11 +108,21 @@ public class BlueToothPortViewModel extends AndroidViewModel { ...@@ -107,11 +108,21 @@ public class BlueToothPortViewModel extends AndroidViewModel {
} }
private void initGetprinterPowerPercentTimer(){ private void initGetprinterPowerPercentTimer() {
getPrinterPowerPercentFullTimerTask = new TimerTask() { getPrinterPowerPercentFullTimerTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
if (getBluetoothDevice().getValue() == null){ if (getBluetoothDevice().getValue() == null) {
return;
}
if (ActivityCompat.checkSelfPermission(getApplication(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return; return;
} }
String deviceName = getBluetoothDevice().getValue().getName(); String deviceName = getBluetoothDevice().getValue().getName();
......
File added
File added
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../../../../.." vcs="Git" />
</component>
</project>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment