Merge branch 'dev_robotaxi-d-app-module_2100_220816_2.10.0' into 'test_robotaxi-d-app-module_2100_220816_2.10.0.1'
Dev robotaxi d app module 2100 220816 2.10.0 See merge request zhjt/AndroidApp/MoGoEagleEye!132
@@ -143,13 +143,6 @@ public class TaxiPassengerBaseFragment extends MvpFragment<TaxiPassengerBaseFrag
|
||||
//OverlayLeftViewUtils.INSTANCE.dismissOverlayView();
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.iv_temp).setOnClickListener(view -> {
|
||||
OverlayLeftViewUtils.INSTANCE.showOverlayView(getActivity(),0,true,-1,true);
|
||||
//showOrHideArrivedEndLayout(true, "北京北京北京", "1527481606997577728");
|
||||
//showOrHidePressengerCheckPager(true, "开始站点开", "开始站点开始站点开始", "2", "京A888888", "18811539480");
|
||||
//OCHFloatWindowManager.getInstance().ShowFloatWindow(getContext());
|
||||
});
|
||||
}
|
||||
|
||||
private void initArrivedView(){
|
||||
|
||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.3 KiB |
BIN
OCH/mogo-och-taxi-passenger/src/main/res/drawable-xhdpi-2560x1440/taxi_p_mogo_movies_title_icon.png
Executable file → Normal file
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.3 KiB |
BIN
OCH/mogo-och-taxi-passenger/src/main/res/drawable-xhdpi/taxi_p_mogo_movies_title_icon.png
Executable file → Normal file
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -305,10 +305,6 @@ public class TaxiModel {
|
||||
subscribe = Observable.timer(5, TimeUnit.SECONDS).subscribe(aLong -> {
|
||||
queryCarStatus();
|
||||
});
|
||||
|
||||
if(mCarStatusCallback!=null) {
|
||||
mCarStatusCallback.onCarLoginStatusChange(StatusManager.isLogin());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,6 +96,10 @@ class TaxiLoginPresenter(view: TaxiLoginDialogFragment?) :
|
||||
mView?.inputPhoneError()
|
||||
return
|
||||
}
|
||||
if(code.isBlank()||code.length<4){
|
||||
ToastUtils.showShort(R.string.module_och_taxi_login_code_error)
|
||||
return
|
||||
}
|
||||
mView?.closeSoftInput()
|
||||
TaxiLoginModel.gotoLogin(phone,code)
|
||||
}
|
||||
|
||||
@@ -112,6 +112,13 @@ public class TaxiPresenter extends Presenter<TaxiFragment> implements ITaxiADASS
|
||||
TaxiModel.getInstance().jumpPassengerCheckDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功后查询登录状态
|
||||
*/
|
||||
public void queryLoginStatus() {
|
||||
TaxiModel.getInstance().queryCarStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 司机确认车辆环境可开启自动驾驶
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.mogo.och.taxi.ui;
|
||||
|
||||
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
@@ -410,9 +411,13 @@ public class TaxiFragment extends BaseTaxiTabFragment<TaxiFragment, TaxiPresente
|
||||
return;
|
||||
}
|
||||
if (taxiLoginDialog.isAdded()) {//解决方法就是添加这行代码,如果已经添加了,就移除掉然后再show,就不会出现Fragment already added的错误了。
|
||||
getParentFragmentManager().beginTransaction().remove(taxiLoginDialog).commit();
|
||||
getParentFragmentManager().beginTransaction().remove(taxiLoginDialog).commitAllowingStateLoss();
|
||||
}
|
||||
taxiLoginDialog.show(getParentFragmentManager(), taxiLoginDialog.getTag());
|
||||
taxiLoginDialog.setOnDismissListener(dialog -> {
|
||||
mPresenter.queryLoginStatus();
|
||||
taxiLoginDialogFragment.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
public void hideLoginDialogFragment(){
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.mogo.och.taxi.ui
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.AppCompatButton
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
@@ -10,6 +12,8 @@ import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.mogo.commons.mvp.MvpDialogFragment
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
|
||||
@@ -18,12 +22,6 @@ import com.mogo.eagle.core.utilcode.util.BarUtils
|
||||
import com.mogo.eagle.core.utilcode.util.KeyboardUtils
|
||||
import com.mogo.och.taxi.R
|
||||
import com.mogo.och.taxi.presenter.TaxiLoginPresenter
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/**
|
||||
@@ -38,6 +36,9 @@ class TaxiLoginDialogFragment : MvpDialogFragment<TaxiLoginDialogFragment?, Taxi
|
||||
lateinit var actvLoginGetCode: AppCompatTextView
|
||||
lateinit var aceLoginPhoneValue: AppCompatEditText
|
||||
lateinit var acetPhoneCodeValue: AppCompatEditText
|
||||
lateinit var actvWelcomeLoginTitle: AppCompatTextView
|
||||
private var mOnClickListener: DialogInterface.OnDismissListener? = null
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -58,12 +59,12 @@ class TaxiLoginDialogFragment : MvpDialogFragment<TaxiLoginDialogFragment?, Taxi
|
||||
}
|
||||
|
||||
override fun initViews() {
|
||||
findViewById<View>(R.id.module_och_taxi_order_list_close_iv)!!.setOnClickListener { v: View? -> dismissAllowingStateLoss() }
|
||||
clMain = mRootView.findViewById(R.id.cl_main)
|
||||
acbtnLogin = mRootView.findViewById(R.id.acbtn_login)
|
||||
actvLoginGetCode = mRootView.findViewById(R.id.actv_login_get_code)
|
||||
aceLoginPhoneValue = mRootView.findViewById(R.id.ace_login_phone_value)
|
||||
acetPhoneCodeValue = mRootView.findViewById(R.id.acet_phone_code_value)
|
||||
actvWelcomeLoginTitle = mRootView.findViewById(R.id.actv_welcome_login_title)
|
||||
inputPhoneNormal()
|
||||
initListener()
|
||||
dialog?.window?.let {
|
||||
@@ -94,6 +95,9 @@ class TaxiLoginDialogFragment : MvpDialogFragment<TaxiLoginDialogFragment?, Taxi
|
||||
val code = acetPhoneCodeValue.text.toString()
|
||||
mPresenter?.gotoLogin(phone,code)
|
||||
}
|
||||
actvWelcomeLoginTitle.setOnClickListener {
|
||||
continuousClick()
|
||||
}
|
||||
clMain.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
val rect = Rect()
|
||||
clMain.getWindowVisibleDisplayFrame(rect)
|
||||
@@ -162,20 +166,18 @@ class TaxiLoginDialogFragment : MvpDialogFragment<TaxiLoginDialogFragment?, Taxi
|
||||
return TaxiLoginPresenter(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
val COUNTS = 4 // 点击次数
|
||||
val DURATION: Long = 1000 // 规定有效时间
|
||||
var mHits = LongArray(COUNTS)
|
||||
private fun continuousClick() {
|
||||
//每次点击时,数组向前移动一位
|
||||
System.arraycopy(mHits, 1, mHits, 0, mHits.size - 1)
|
||||
//为数组最后一位赋值
|
||||
mHits[mHits.size-1] = SystemClock.uptimeMillis()
|
||||
if (mHits[0] >= (SystemClock.uptimeMillis() - DURATION)) {
|
||||
mHits = LongArray(COUNTS) //重新初始化数组
|
||||
mPresenter?.gotoLogin("13288888888","8888")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKey(dialog: DialogInterface, keyCode: Int, event: KeyEvent): Boolean {
|
||||
@@ -186,6 +188,37 @@ class TaxiLoginDialogFragment : MvpDialogFragment<TaxiLoginDialogFragment?, Taxi
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
fun setOnDismissListener(listener: DialogInterface.OnDismissListener?) {
|
||||
mOnClickListener = listener
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
mOnClickListener?.onDismiss(dialog)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写父类show()方法
|
||||
* 避免出现java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
|
||||
*/
|
||||
override fun show(manager: FragmentManager, tag: String?) {
|
||||
try {
|
||||
val cls = this.javaClass.superclass ?: return
|
||||
val mDismissed = cls.getDeclaredField("mDismissed")
|
||||
val mShownByMe = cls.getDeclaredField("mShownByMe")
|
||||
mDismissed.isAccessible = true
|
||||
mShownByMe.isAccessible = true
|
||||
mDismissed.setBoolean(this, false)
|
||||
mShownByMe.setBoolean(this, true)
|
||||
} catch (e: Exception) {
|
||||
Log.e("DialogFragment", "show", e.fillInStackTrace())
|
||||
}
|
||||
val ft: FragmentTransaction = manager.beginTransaction()
|
||||
ft.add(this, tag)
|
||||
ft.commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val TAG = TaxiLoginDialogFragment::class.java.simpleName
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 44 KiB |
@@ -115,13 +115,5 @@
|
||||
android:layout_width="760px"
|
||||
android:layout_height="136px"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/module_och_taxi_order_list_close_iv"
|
||||
android:layout_width="150px"
|
||||
android:layout_height="150px"
|
||||
android:layout_gravity="right"
|
||||
android:padding="30px"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
<string name="module_och_taxi_login_get_code_success">获取验证码成功</string>
|
||||
<string name="module_och_taxi_login_login_success">登录成功</string>
|
||||
<string name="module_och_taxi_login_phone_error">请输入正确的手机号</string>
|
||||
<string name="module_och_taxi_login_code_error">请输入正确的验证码</string>
|
||||
<string name="module_och_taxi_login_phone_hint_text">请输入手机号</string>
|
||||
<string name="module_och_taxi_login_get_phone_title">请输入手机号</string>
|
||||
|
||||
|
||||
@@ -243,6 +243,9 @@ dependencies {
|
||||
debugImplementation rootProject.ext.dependencies.debugleakcanary
|
||||
releaseImplementation rootProject.ext.dependencies.releaseleakcanary
|
||||
implementation rootProject.ext.dependencies.android_start_up
|
||||
|
||||
// // 暂不使用Shizuku-API
|
||||
// implementation rootProject.ext.dependencies.shizuku_provider
|
||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||
implementation rootProject.ext.dependencies.ttspad
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_hmi
|
||||
|
||||
@@ -31,6 +31,15 @@
|
||||
android:value="android.startup.provider.config" />
|
||||
</provider>
|
||||
|
||||
<!-- <!– 暂不使用Shizuku-API –>-->
|
||||
<!-- <provider-->
|
||||
<!-- android:name="rikka.shizuku.ShizukuProvider"-->
|
||||
<!-- android:authorities="${applicationId}.shizuku"-->
|
||||
<!-- android:enabled="true"-->
|
||||
<!-- android:exported="true"-->
|
||||
<!-- android:multiprocess="false"-->
|
||||
<!-- android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />-->
|
||||
|
||||
<!-- 配置APP ID -->
|
||||
<meta-data
|
||||
android:name="BUGLY_APPID"
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.mogo.launcher;
|
||||
|
||||
import com.mogo.eagle.core.function.main.MainMoGoApplication;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.LogLevel;
|
||||
@@ -11,11 +10,7 @@ import com.mogo.launcher.crash.CrashSystem;
|
||||
import com.mogo.thread.ext.core.ThreadManager;
|
||||
import com.mogo.thread.ext.core.config.ThreadConfig;
|
||||
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
|
||||
@@ -61,6 +61,8 @@ class HttpDnsStartUp : AndroidStartup<Boolean>() {
|
||||
|
||||
private var context: Context? = null
|
||||
|
||||
private var gotToken = false
|
||||
|
||||
override fun callCreateOnMainThread() = true
|
||||
|
||||
override fun waitOnMainThread() = false
|
||||
@@ -198,15 +200,19 @@ class HttpDnsStartUp : AndroidStartup<Boolean>() {
|
||||
MoGoAiCloudClient.getInstance().init(context, clientConfig).addTokenCallbacks(
|
||||
object : IMoGoTokenCallback {
|
||||
override fun onTokenGot(token: String, sn: String) {
|
||||
CallerLogger.d(SceneConstant.M_HMI + TAG, "onTokenGot ")
|
||||
clientConfig.token = token
|
||||
CallerCloudListenerManager.invokeCloudTokenGot(token)
|
||||
// 异步初始化NetConfig
|
||||
asyncInit()
|
||||
// HttpDns ttl回调 --- socketTTL
|
||||
registerSocketHttpDnsTTL(HostConst.SOCKET_CENTER_DOMAIN)
|
||||
// 开启每5s/次定位上报
|
||||
uploadLocPerFiveSecond()
|
||||
// 由于存在token过期问题,在更新后会回调至此处,增加二次判定
|
||||
if(!gotToken){
|
||||
CallerLogger.d(SceneConstant.M_MAP + TAG, "onTokenGot ")
|
||||
CallerCloudListenerManager.invokeCloudTokenGot(token)
|
||||
// 异步初始化NetConfig
|
||||
asyncInit()
|
||||
// HttpDns ttl回调 --- socketTTL
|
||||
registerSocketHttpDnsTTL(HostConst.SOCKET_CENTER_DOMAIN)
|
||||
// 开启每5s/次定位上报
|
||||
uploadLocPerFiveSecond()
|
||||
gotToken = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(code: Int, msg: String) {
|
||||
|
||||
1
app_mogo_magic_ring/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
1
app_mogo_magic_ring/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# 工控机测试程序
|
||||
72
app_mogo_magic_ring/build.gradle
Normal file
@@ -0,0 +1,72 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
defaultConfig {
|
||||
applicationId "com.zhidao.adas.magic"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
multiDexEnabled true
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
||||
packagingOptions {
|
||||
//解决编译时com.android.builder.merge.DuplicateRelativeFileException: More than one file was found with OS independent path 'META-INF/rxjava.properties'这个错误
|
||||
exclude 'META-INF/rxjava.properties'
|
||||
}
|
||||
externalNativeBuild {
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
//插入构建时间
|
||||
buildTypes.each {
|
||||
//设置时间格式
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd.HHmmss", Locale.getDefault())
|
||||
//获取当前时间
|
||||
Date curDate = new Date(System.currentTimeMillis())
|
||||
String buildTime = formatter.format(curDate)
|
||||
it.buildConfigField 'String', 'BUILD_TIME', "\"${buildTime}\""
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
// Or, if you prefer, you can continue to check for errors in release builds,
|
||||
// but continue the build even when errors are found:
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation rootProject.ext.dependencies.material
|
||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
implementation rootProject.ext.dependencies.gson
|
||||
implementation project(':libraries:mogo-adas')
|
||||
compileOnly project(':core:mogo-core-data')
|
||||
implementation project(':core:mogo-core-utils')
|
||||
implementation 'org.greenrobot:eventbus:3.2.0'
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
}
|
||||
26
app_mogo_magic_ring/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# 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 *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
#-----MogoMap-----
|
||||
-keep class com.mogo.map.MogoNavi{
|
||||
private <init>();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zhidao.adas.magic;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.zhidao.adas.magic", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
36
app_mogo_magic_ring/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.zhidao.adas.magic">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zhidao.adas.magic;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.zhidao.adas.magic.utils.CrashHandler;
|
||||
|
||||
public class App extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
CrashHandler.getInstance().init(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.zhidao.adas.magic.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhidao.adas.magic.R;
|
||||
import com.zhidao.adas.magic.base.BaseAdapter;
|
||||
import com.zhidao.adas.magic.base.BaseViewHolder;
|
||||
import com.zhidao.adas.magic.bean.IPCConnectState;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
* 连接状态
|
||||
*/
|
||||
public class ConnectStatusAdapter extends BaseAdapter<IPCConnectState, ConnectStatusAdapter.ViewHolder> {
|
||||
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ", Locale.getDefault());
|
||||
private static final String POS = "%03d. ";
|
||||
|
||||
public void refreshView() {
|
||||
// notifyItemChanged(mDatas.size()-1,0);
|
||||
notifyItemRangeChanged(0, getItemCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
protected void onBindDataToItem(ViewHolder viewHolder, IPCConnectState data, int position) {
|
||||
viewHolder.id.setText(String.format(Locale.getDefault(), POS, getItemCount() - position));
|
||||
viewHolder.editText.setText(sdf.format(new Date(data.time)) + "\n" + data.status);
|
||||
viewHolder.editText.setTextColor(mContext.getResources().getColor(data.color));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getItemViewResource(ViewGroup viewGroup) {
|
||||
return LayoutInflater.from(mContext).inflate(R.layout.item_status, viewGroup, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ViewHolder getViewHolder(View view) {
|
||||
return new ViewHolder(view, this);
|
||||
}
|
||||
|
||||
|
||||
class ViewHolder extends BaseViewHolder<ConnectStatusAdapter> {
|
||||
EditText editText;
|
||||
TextView id;
|
||||
|
||||
public ViewHolder(View itemView, ConnectStatusAdapter adapter) {
|
||||
super(itemView, adapter);
|
||||
editText = itemView.findViewById(R.id.log);
|
||||
id = itemView.findViewById(R.id.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.zhidao.adas.magic.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.zhidao.adas.magic.R;
|
||||
import com.zhidao.adas.magic.base.BaseAdapter;
|
||||
import com.zhidao.adas.magic.base.BaseViewHolder;
|
||||
import com.zhidao.adas.magic.bean.TitleBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song kenan
|
||||
* @des
|
||||
* @date 2021/8/13
|
||||
*/
|
||||
public class InfoTitleAdapter extends BaseAdapter<TitleBean, InfoTitleAdapter.ViewHolder> {
|
||||
|
||||
public void setSelectedPosition(int position) {
|
||||
notifyItemChanged(position);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public InfoTitleAdapter(List<TitleBean> data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindDataToItem(ViewHolder viewHolder, TitleBean data, int position) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
viewHolder.itemView.setSelected(data.isSelected);
|
||||
viewHolder.title.setText(data.name);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getItemViewResource(ViewGroup viewGroup) {
|
||||
|
||||
|
||||
return LayoutInflater.from(mContext).inflate(R.layout.item_info, viewGroup, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewHolder getViewHolder(View view) {
|
||||
return new ViewHolder(view, this);
|
||||
}
|
||||
|
||||
class ViewHolder extends BaseViewHolder<InfoTitleAdapter> {
|
||||
TextView title;
|
||||
|
||||
public ViewHolder(View itemView, InfoTitleAdapter adapter) {
|
||||
super(itemView, adapter);
|
||||
title = itemView.findViewById(R.id.tv_info_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.zhidao.adas.magic.base;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.view.Gravity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.zhidao.adas.magic.ui.MainActivity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
private BaseHandler mBaseHandler;
|
||||
private Toast toast;
|
||||
|
||||
protected void showToastCenter(String msg) {
|
||||
showToastCenter(msg, Toast.LENGTH_SHORT);
|
||||
}
|
||||
|
||||
protected void showToastCenter(String msg, int duration) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (toast != null) {
|
||||
toast.cancel();
|
||||
toast = null;
|
||||
}
|
||||
toast = Toast.makeText(BaseActivity.this, "", duration); //如果有居中显示需求
|
||||
toast.setGravity(Gravity.CENTER, 0, 0);
|
||||
toast.setText(msg);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (getHandler() != null)
|
||||
getHandler().removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化一个Handler,如果需要使用Handler,先调用此方法,
|
||||
* 然后可以使用postRunnable(Runnable runnable),
|
||||
* sendMessage在handleMessage(Message msg)中接收msg
|
||||
*/
|
||||
public void initHandler() {
|
||||
mBaseHandler = new BaseHandler(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回Handler,在此之前确定已经调用initHandler()
|
||||
*
|
||||
* @return Handler
|
||||
*/
|
||||
public Handler getHandler() {
|
||||
return mBaseHandler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同Handler 的 handleMessage,
|
||||
* getHandler.sendMessage,发送的Message在此接收
|
||||
* 在此之前确定已经调用initHandler()
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
protected void handleMessage(Message msg) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 同Handler的postRunnable
|
||||
* 在此之前确定已经调用initHandler()
|
||||
*/
|
||||
protected void postRunnable(Runnable runnable) {
|
||||
postRunnableDelayed(runnable, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同Handler的postRunnableDelayed
|
||||
* 在此之前确定已经调用initHandler()
|
||||
*/
|
||||
protected void postRunnableDelayed(Runnable runnable, long delayMillis) {
|
||||
if (mBaseHandler == null) initHandler();
|
||||
mBaseHandler.postDelayed(runnable, delayMillis);
|
||||
}
|
||||
|
||||
|
||||
protected static class BaseHandler extends Handler {
|
||||
private final WeakReference<BaseActivity> mObjects;
|
||||
|
||||
public BaseHandler(BaseActivity mPresenter) {
|
||||
mObjects = new WeakReference<BaseActivity>(mPresenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
BaseActivity mPresenter = mObjects.get();
|
||||
if (mPresenter != null)
|
||||
mPresenter.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.zhidao.adas.magic.base;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* RecycleView Adapter
|
||||
* Created by renfeicui on 2018/10/12.
|
||||
*/
|
||||
public abstract class BaseAdapter<D, VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> {
|
||||
protected String TAG = this.getClass().getSimpleName();
|
||||
protected List<D> mDatas;
|
||||
protected Context mContext;
|
||||
private OnItemClickListener<D> mItemClick;
|
||||
|
||||
public interface OnItemClickListener<D> {
|
||||
void onItemClick(int position, D data);
|
||||
|
||||
boolean onItemLongClick(int position, D data);
|
||||
}
|
||||
|
||||
|
||||
public BaseAdapter() {
|
||||
}
|
||||
|
||||
public BaseAdapter(List<D> mDatas) {
|
||||
this.mDatas = mDatas;
|
||||
}
|
||||
|
||||
public BaseAdapter(OnItemClickListener<D> listener) {
|
||||
mItemClick = listener;
|
||||
}
|
||||
|
||||
public BaseAdapter(List<D> mDatas, OnItemClickListener<D> listener) {
|
||||
this.mDatas = mDatas;
|
||||
mItemClick = listener;
|
||||
}
|
||||
|
||||
public void setData(List<D> mDatas) {
|
||||
this.mDatas = mDatas;
|
||||
if (!mDatas.isEmpty())
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener<D> listener) {
|
||||
mItemClick = listener;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取制定 位置的Data
|
||||
* @param position 下标
|
||||
* @return Data
|
||||
*/
|
||||
public D getItem(int position) {
|
||||
return mDatas == null ? null : mDatas.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mDatas == null ? 0 : mDatas.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH viewHolder, int position) {
|
||||
D bean = getItem(position);
|
||||
onBindDataToItem(viewHolder, bean, position);
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
|
||||
mContext = viewGroup.getContext();
|
||||
return getViewHolder(getItemViewResource(viewGroup));
|
||||
}
|
||||
|
||||
/***
|
||||
* 同onBindViewHolder()
|
||||
* @param viewHolder viewHolder
|
||||
* @param data 数据
|
||||
* @param position 下标
|
||||
*/
|
||||
protected abstract void onBindDataToItem(VH viewHolder, D data, int position);
|
||||
|
||||
/***
|
||||
* 获取Item布局
|
||||
* @return id
|
||||
*/
|
||||
protected abstract View getItemViewResource(ViewGroup viewGroup);
|
||||
|
||||
/**
|
||||
* 获取ViewHolder
|
||||
*
|
||||
* @param view
|
||||
* @return
|
||||
*/
|
||||
protected abstract VH getViewHolder(View view);
|
||||
|
||||
public void onClick(BaseViewHolder viewHolder) {
|
||||
if (mItemClick != null) {
|
||||
mItemClick.onItemClick(viewHolder.getBindingAdapterPosition(), getItem(viewHolder.getBindingAdapterPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onLongClick(BaseViewHolder viewHolder) {
|
||||
if (mItemClick != null) {
|
||||
return mItemClick.onItemLongClick(viewHolder.getBindingAdapterPosition(), getItem(viewHolder.getBindingAdapterPosition()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.zhidao.adas.magic.base;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
||||
public abstract class BaseViewHolder<T extends BaseAdapter> extends RecyclerView.ViewHolder {
|
||||
private T adapter;
|
||||
public View itemView;
|
||||
|
||||
public BaseViewHolder(View itemView, final T adapter) {
|
||||
super(itemView);
|
||||
this.itemView = itemView;
|
||||
this.adapter = adapter;
|
||||
itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
adapter.onClick(BaseViewHolder.this);
|
||||
}
|
||||
});
|
||||
itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
return adapter.onLongClick(BaseViewHolder.this);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zhidao.adas.magic.bean;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
|
||||
public class IPCConnectState {
|
||||
public final long time;
|
||||
public final String status;
|
||||
@ColorRes
|
||||
public final int color;
|
||||
|
||||
public IPCConnectState(String status, @ColorRes int color) {
|
||||
this.status = status;
|
||||
this.color = color;
|
||||
time = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.zhidao.adas.magic.bean;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class TitleBean {
|
||||
public final String name;
|
||||
public boolean isSelected = false;
|
||||
|
||||
public TitleBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TitleBean titleBean = (TitleBean) o;
|
||||
return Objects.equals(name, titleBean.name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.zhidao.adas.magic.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.zhidao.adas.magic.R;
|
||||
import com.zhidao.adas.magic.adapter.ConnectStatusAdapter;
|
||||
import com.zhidao.adas.magic.bean.IPCConnectState;
|
||||
import com.zhidao.adas.magic.utils.MyLinearLayoutManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xuxinchao
|
||||
* @description
|
||||
* @since: 2022/4/20
|
||||
*/
|
||||
public class FloatWindow implements View.OnTouchListener {
|
||||
|
||||
private final Activity mContext;
|
||||
private WindowManager.LayoutParams mWindowParams;
|
||||
private WindowManager mWindowManager;
|
||||
|
||||
private View mFloatLayout;
|
||||
private float mInViewX;
|
||||
private float mInViewY;
|
||||
private float mDownInScreenX;
|
||||
private float mDownInScreenY;
|
||||
private float mInScreenX;
|
||||
private float mInScreenY;
|
||||
private RecyclerView rv_status;
|
||||
private ConnectStatusAdapter adapter;
|
||||
private List<IPCConnectState> list;
|
||||
|
||||
public FloatWindow(Activity context, List<IPCConnectState> list) {
|
||||
this.mContext = context;
|
||||
this.list = list;
|
||||
initFloatWindow();
|
||||
}
|
||||
|
||||
public void refreshView() {
|
||||
if (adapter != null) {
|
||||
adapter.refreshView();
|
||||
}
|
||||
}
|
||||
|
||||
private void initRV() {
|
||||
//创建默认的线性LayoutManager 横向的GridLayoutManager
|
||||
MyLinearLayoutManager linearLayoutManager = new MyLinearLayoutManager(mContext);
|
||||
// linearLayoutManager.setStackFromEnd(true);//列表再底部开始展示,反转后由上面开始展示
|
||||
// linearLayoutManager.setReverseLayout(true);//列表翻转
|
||||
rv_status.setLayoutManager(linearLayoutManager);
|
||||
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
|
||||
rv_status.setHasFixedSize(false);
|
||||
rv_status.setNestedScrollingEnabled(false);
|
||||
adapter = new ConnectStatusAdapter();
|
||||
adapter.setHasStableIds(true);
|
||||
rv_status.setAdapter(adapter);
|
||||
adapter.setData(list);
|
||||
}
|
||||
|
||||
private void initFloatWindow() {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
if (inflater == null)
|
||||
return;
|
||||
mFloatLayout = (View) inflater.inflate(R.layout.layout_float, null);
|
||||
rv_status = mFloatLayout.findViewById(R.id.rv_status);
|
||||
mFloatLayout.setOnTouchListener(this);
|
||||
initRV();
|
||||
mWindowParams = new WindowManager.LayoutParams();
|
||||
// mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
// if (Build.VERSION.SDK_INT >= 26) {//8.0新特性
|
||||
// mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
// }else{
|
||||
// mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
|
||||
// }
|
||||
|
||||
mWindowManager = mContext.getWindowManager();
|
||||
|
||||
mWindowParams.format = PixelFormat.RGBA_8888;
|
||||
mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
mWindowParams.gravity = Gravity.START | Gravity.TOP;
|
||||
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
mWindowParams.alpha = 0.9F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
return floatLayoutTouch(motionEvent);
|
||||
}
|
||||
|
||||
private boolean floatLayoutTouch(MotionEvent motionEvent) {
|
||||
switch (motionEvent.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 获取相对View的坐标,即以此View左上角为原点
|
||||
mInViewX = motionEvent.getX();
|
||||
mInViewY = motionEvent.getY();
|
||||
// 获取相对屏幕的坐标,即以屏幕左上角为原点
|
||||
mDownInScreenX = motionEvent.getRawX();
|
||||
mDownInScreenY = motionEvent.getRawY() - getSysBarHeight(mContext);
|
||||
mInScreenX = motionEvent.getRawX();
|
||||
mInScreenY = motionEvent.getRawY() - getSysBarHeight(mContext);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 更新浮动窗口位置参数
|
||||
mInScreenX = motionEvent.getRawX();
|
||||
mInScreenY = motionEvent.getRawY() - getSysBarHeight(mContext);
|
||||
mWindowParams.x = (int) (mInScreenX - mInViewX);
|
||||
mWindowParams.y = (int) (mInScreenY - mInViewY);
|
||||
// 手指移动的时候更新小悬浮窗的位置
|
||||
mWindowManager.updateViewLayout(mFloatLayout, mWindowParams);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
// 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
|
||||
if (mDownInScreenX == mInScreenX && mDownInScreenY == mInScreenY) {
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void showFloatWindow(float y) {
|
||||
if (mFloatLayout.getParent() == null) {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
// 默认固定位置,靠屏幕右边缘的中间
|
||||
mWindowManager.getDefaultDisplay().getMetrics(metrics);
|
||||
// mWindowParams.x = metrics.widthPixels;
|
||||
mWindowParams.x = 0;
|
||||
mWindowParams.y = (int) (y);
|
||||
mWindowManager.addView(mFloatLayout, mWindowParams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void hideFloatWindow() {
|
||||
if (mFloatLayout.getParent() != null)
|
||||
mWindowManager.removeView(mFloatLayout);
|
||||
}
|
||||
|
||||
public void setFloatLayoutAlpha(boolean alpha) {
|
||||
if (alpha)
|
||||
mFloatLayout.setAlpha((float) 0.5);
|
||||
else
|
||||
mFloatLayout.setAlpha(1);
|
||||
}
|
||||
|
||||
private int sbar = -1;
|
||||
|
||||
// 获取系统状态栏高度
|
||||
public int getSysBarHeight(Context contex) {
|
||||
if (sbar == -1) {
|
||||
Class<?> c;
|
||||
Object obj;
|
||||
Field field;
|
||||
int x;
|
||||
sbar = 0;
|
||||
try {
|
||||
c = Class.forName("com.android.internal.R$dimen");
|
||||
obj = c.newInstance();
|
||||
field = c.getField("status_bar_height");
|
||||
x = Integer.parseInt(field.get(obj).toString());
|
||||
sbar = contex.getResources().getDimensionPixelSize(x);
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
return sbar;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package com.zhidao.adas.magic.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider
|
||||
* between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and
|
||||
* {@link #VERTICAL} orientations.
|
||||
*
|
||||
* <pre>
|
||||
* mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
|
||||
* mLayoutManager.getOrientation());
|
||||
* recyclerView.addItemDecoration(mDividerItemDecoration);
|
||||
* </pre>
|
||||
*/
|
||||
public class HorizontalDividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
|
||||
public static final int VERTICAL = LinearLayout.VERTICAL;
|
||||
|
||||
private static final String TAG = "DividerItem";
|
||||
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
|
||||
|
||||
private Drawable mDivider;
|
||||
|
||||
|
||||
private final Rect mBounds = new Rect();
|
||||
|
||||
/**
|
||||
* Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
|
||||
* {@link LinearLayoutManager}.
|
||||
*
|
||||
* @param context Current context, it will be used to access resources.
|
||||
*/
|
||||
public HorizontalDividerItemDecoration(Context context) {
|
||||
final TypedArray a = context.obtainStyledAttributes(ATTRS);
|
||||
mDivider = a.getDrawable(0);
|
||||
if (mDivider == null) {
|
||||
Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "
|
||||
+ "DividerItemDecoration. Please set that attribute all call setDrawable()");
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the {@link Drawable} for this divider.
|
||||
*
|
||||
* @param drawable Drawable that should be used as a divider.
|
||||
*/
|
||||
public void setDrawable(@NonNull Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
throw new IllegalArgumentException("Drawable cannot be null.");
|
||||
}
|
||||
mDivider = drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Drawable} for this divider.
|
||||
*/
|
||||
@Nullable
|
||||
public Drawable getDrawable() {
|
||||
return mDivider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
if (parent.getLayoutManager() == null || mDivider == null) {
|
||||
return;
|
||||
}
|
||||
drawHorizontal(c, parent);
|
||||
}
|
||||
|
||||
|
||||
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
|
||||
canvas.save();
|
||||
final int top;
|
||||
final int bottom;
|
||||
//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
|
||||
if (parent.getClipToPadding()) {
|
||||
top = parent.getPaddingTop();
|
||||
bottom = parent.getHeight() - parent.getPaddingBottom();
|
||||
canvas.clipRect(parent.getPaddingLeft(), top,
|
||||
parent.getWidth() - parent.getPaddingRight(), bottom);
|
||||
} else {
|
||||
top = 0;
|
||||
bottom = parent.getHeight();
|
||||
}
|
||||
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; ) {
|
||||
final View child = parent.getChildAt(i);
|
||||
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);
|
||||
final int right = mBounds.right + Math.round(child.getTranslationX());
|
||||
final int left = right - mDivider.getIntrinsicWidth();
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(canvas);
|
||||
i += 2;
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
|
||||
RecyclerView.State state) {
|
||||
if (mDivider == null) {
|
||||
outRect.set(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,807 @@
|
||||
package com.zhidao.adas.magic.ui;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.provider.Settings;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListPopupWindow;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils;
|
||||
import com.zhidao.adas.magic.R;
|
||||
import com.zhidao.adas.magic.adapter.InfoTitleAdapter;
|
||||
import com.zhidao.adas.magic.base.BaseActivity;
|
||||
import com.zhidao.adas.magic.base.BaseAdapter;
|
||||
import com.zhidao.adas.magic.bean.IPCConnectState;
|
||||
import com.zhidao.adas.magic.bean.TitleBean;
|
||||
import com.zhidao.adas.magic.utils.Constants;
|
||||
import com.zhidao.support.adas.high.AdasManager;
|
||||
import com.zhidao.support.adas.high.AdasOptions;
|
||||
import com.zhidao.support.adas.high.OnAdasConnectStatusListener;
|
||||
import com.zhidao.support.adas.high.OnAdasListener;
|
||||
import com.zhidao.support.adas.high.bean.VersionCompatibility;
|
||||
import com.zhidao.support.adas.high.common.Constants.IPC_CONNECTION_STATUS;
|
||||
import com.zhidao.support.adas.high.common.CupidLogUtils;
|
||||
import com.zhidao.support.adas.high.common.ProtocolStatus;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import chassis.VehicleStateOuterClass;
|
||||
import mogo.telematics.pad.MessagePad;
|
||||
import mogo_msg.MogoReportMsg;
|
||||
import perception.TrafficLightOuterClass;
|
||||
import prediction.Prediction;
|
||||
import record_cache.RecordPanelOuterClass;
|
||||
import rule_segement.MogoPointCloudOuterClass;
|
||||
import system_master.SystemStatusInfo;
|
||||
|
||||
public class MainActivity extends BaseActivity implements OnAdasListener, OnAdasConnectStatusListener, BaseAdapter.OnItemClickListener<TitleBean> {
|
||||
private final static String TAG = MainActivity.class.getSimpleName();
|
||||
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault());
|
||||
private static final String GNSS_HINT = "%s\n经度:%f 纬度:%f 海拔:%f 航向角:%f 加速度:%f 曲率:%f 惯导车速:%f 车辆车速:%f";
|
||||
|
||||
private static final int WHAT_IPC_IP = 0x00;
|
||||
private static final int WHAT_IPC_CONNECT_STATE = 0x01;
|
||||
private static final int WHAT_UPDATE_GNSS = 0x02;
|
||||
private EditText etIp;
|
||||
private ImageView tvIp;
|
||||
private TextView title;
|
||||
private TextView ipcIp;
|
||||
private TextView localIp;
|
||||
private RadioGroup connectionType;
|
||||
private AppCompatButton connect;
|
||||
private AppCompatButton disconnect;
|
||||
private RadioButton fixation;
|
||||
private RadioButton assign;
|
||||
private RecyclerView infoBtn;
|
||||
private TextView tvConnectState;
|
||||
private TextView gnss_hint;
|
||||
private InfoTitleAdapter btnAdapter;
|
||||
private Timer timerHorn;
|
||||
private Timer timerAcc;
|
||||
private final List<TitleBean> titleBtnData = new ArrayList<>();
|
||||
private final List<IPCConnectState> connectStatusList = new ArrayList<>();
|
||||
private boolean isPad;
|
||||
private int connectStatus;
|
||||
private ListPopupWindow listPopupWindow;
|
||||
private FloatWindow floatWindow;
|
||||
private View include_title;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
initHandler();
|
||||
isPad = isPad(this);
|
||||
if (!isPad)
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
initView();
|
||||
initAdas();
|
||||
connectStatus = AdasManager.getInstance().getIpcConnectionStatus();
|
||||
onUpdateConnectStateView();
|
||||
showIPCIP();
|
||||
AdasManager.getInstance().setEnableLog(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
AdasManager.getInstance().setOnAdasListener(null);
|
||||
AdasManager.getInstance().disconnect();
|
||||
if (floatWindow != null) {
|
||||
floatWindow.hideFloatWindow();
|
||||
floatWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canDrawOverlays() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("权限申请")
|
||||
.setMessage("请找到“" + getString(R.string.app_name) + "”授予悬浮窗权限")
|
||||
.setPositiveButton("去授予",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())));
|
||||
}
|
||||
}).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void connect(boolean isConnect) {
|
||||
if (isConnect) {
|
||||
switch (Constants.getIpcConnectionMode(this)) {
|
||||
case AdasOptions.IPC_CONNECTION_MODE.FIXATION:
|
||||
AdasManager.getInstance().getAdasOptions().setIpcFixationIP(AdasManager.getInstance().getIPCFixationIPList(this));
|
||||
break;
|
||||
case AdasOptions.IPC_CONNECTION_MODE.ASSIGN:
|
||||
String ip = etIp.getText().toString().trim();
|
||||
if (TextUtils.isEmpty(ip)) {
|
||||
Toast.makeText(this, "请输入指定IP", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
AdasManager.getInstance().getAdasOptions().setIpcAssignIP(ip);
|
||||
break;
|
||||
}
|
||||
AdasManager.getInstance().getAdasOptions().setIpcConnectionMode(Constants.getIpcConnectionMode(this));
|
||||
AdasManager.getInstance().connect();
|
||||
} else
|
||||
AdasManager.getInstance().disconnect();
|
||||
}
|
||||
|
||||
|
||||
private void initView() {
|
||||
gnss_hint = findViewById(R.id.gnss_hint);
|
||||
include_title = findViewById(R.id.include_title);
|
||||
etIp = findViewById(R.id.et_ip);
|
||||
connectionType = findViewById(R.id.connection_type);
|
||||
tvIp = findViewById(R.id.tv_ip);
|
||||
connect = findViewById(R.id.connect);
|
||||
disconnect = findViewById(R.id.disconnect);
|
||||
fixation = findViewById(R.id.fixation);
|
||||
assign = findViewById(R.id.assign);
|
||||
|
||||
title = findViewById(R.id.title);
|
||||
infoBtn = findViewById(R.id.info_btn);
|
||||
tvConnectState = findViewById(R.id.tv_connect_state);
|
||||
ipcIp = findViewById(R.id.ipc_ip);
|
||||
localIp = findViewById(R.id.local_ip);
|
||||
|
||||
connect.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
connect(true);
|
||||
}
|
||||
});
|
||||
disconnect.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
connect(false);
|
||||
}
|
||||
});
|
||||
initListData();
|
||||
initBtnRecyclerView();
|
||||
switch (Constants.getIpcConnectionMode(this)) {
|
||||
case 0:
|
||||
fixation.setChecked(true);
|
||||
break;
|
||||
case 1:
|
||||
assign.setChecked(true);
|
||||
break;
|
||||
|
||||
}
|
||||
String ip = Constants.getIPCIp(this);
|
||||
if (!TextUtils.isEmpty(ip)) {
|
||||
etIp.setText(ip);
|
||||
etIp.setSelection(ip.length());
|
||||
}
|
||||
|
||||
|
||||
connectionType.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
AdasManager.getInstance().disconnect();
|
||||
int type;
|
||||
switch (checkedId) {
|
||||
default:
|
||||
case R.id.assign:
|
||||
type = AdasOptions.IPC_CONNECTION_MODE.ASSIGN;
|
||||
break;
|
||||
case R.id.fixation:
|
||||
type = AdasOptions.IPC_CONNECTION_MODE.FIXATION;
|
||||
break;
|
||||
}
|
||||
Constants.setIpcConnectionMode(MainActivity.this, type);
|
||||
}
|
||||
});
|
||||
etIp.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (TextUtils.isEmpty(s)) {
|
||||
Constants.delIPCIp(MainActivity.this);
|
||||
} else {
|
||||
String str = s.toString();
|
||||
if (str.contains(":")) {
|
||||
str = str.replace(":", ":");
|
||||
etIp.setText(str);
|
||||
etIp.setSelection(str.length());
|
||||
}
|
||||
Constants.setIPCIp(MainActivity.this, str);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
showLocalIP();
|
||||
title.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showLocalIP();
|
||||
}
|
||||
});
|
||||
|
||||
tvIp.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listPopupWindow == null) {
|
||||
tvIp.setSelected(true);
|
||||
showListPopupWindow();
|
||||
} else {
|
||||
tvIp.setSelected(false);
|
||||
listPopupWindow.dismiss();
|
||||
listPopupWindow = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
tvConnectState.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!canDrawOverlays()) {
|
||||
return;
|
||||
}
|
||||
if (floatWindow == null) {
|
||||
floatWindow = new FloatWindow(MainActivity.this, connectStatusList);
|
||||
floatWindow.showFloatWindow(include_title.getY() + include_title.getHeight());
|
||||
} else {
|
||||
floatWindow.hideFloatWindow();
|
||||
floatWindow = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void showListPopupWindow() {
|
||||
List<String> ips = Constants.getIpcUsedIps(this);
|
||||
if (ips != null && !ips.isEmpty()) {
|
||||
listPopupWindow = new ListPopupWindow(this);
|
||||
listPopupWindow.setAdapter(new ArrayAdapter<String>(this, R.layout.item_pop, ips));
|
||||
listPopupWindow.setAnchorView(etIp);//以哪个控件为基准,在该处以mEditText为基准
|
||||
listPopupWindow.setModal(true);
|
||||
listPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
listPopupWindow = null;
|
||||
tvIp.setSelected(false);
|
||||
}
|
||||
});
|
||||
listPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
String text = ips.get(i);
|
||||
etIp.setText(text);
|
||||
etIp.setSelection(text.length());
|
||||
listPopupWindow.dismiss();
|
||||
listPopupWindow = null;
|
||||
}
|
||||
});
|
||||
listPopupWindow.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void initListData() {
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_RESTORATION));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_ACCELERATED_SPEED_1));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_ACCELERATED_SPEED_2));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_CHANGE_LANE_LEFT));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_CHANGE_LANE_RIGHT));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.SEND_HORN));
|
||||
titleBtnData.add(new TitleBean(Constants.TITLE.START_AUTOPILOT));
|
||||
}
|
||||
|
||||
private void initBtnRecyclerView() {
|
||||
//初始info-recycle
|
||||
GridLayoutManager nodLinearLayoutManage = new GridLayoutManager(this, 3);
|
||||
nodLinearLayoutManage.setOrientation(LinearLayoutManager.VERTICAL);
|
||||
infoBtn.setLayoutManager(nodLinearLayoutManage);
|
||||
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
|
||||
infoBtn.setHasFixedSize(true);
|
||||
//解决局部刷新闪屏问题
|
||||
SimpleItemAnimator animatorInfo = (SimpleItemAnimator) infoBtn.getItemAnimator();
|
||||
if (animatorInfo != null)
|
||||
animatorInfo.setSupportsChangeAnimations(false);
|
||||
//创建并设置Adapter
|
||||
btnAdapter = new InfoTitleAdapter(titleBtnData);
|
||||
infoBtn.setAdapter(btnAdapter);
|
||||
btnAdapter.setOnItemClickListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 感知接口 提示
|
||||
*
|
||||
* @param isSeriaNet 是否是透传接口
|
||||
*/
|
||||
private void hintTrackedObjects(boolean isSeriaNet) {
|
||||
int value = 0;
|
||||
MessagePad.CarConfigResp carConfigResp = AdasManager.getInstance().getCarConfig();
|
||||
if (carConfigResp != null) {
|
||||
value = carConfigResp.getProtocolVersionValue();
|
||||
}
|
||||
if (isSeriaNet && value < 3 && value > 0 || value > 2) {
|
||||
showToastCenter("当前工控机协议版本:" + value + ",此接口不受支持");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getIPCIP() {
|
||||
final String ip = AdasManager.getInstance().getIpcConnectedIp();
|
||||
final int port = AdasManager.getInstance().getIpcConnectedPort();
|
||||
String temp = "";
|
||||
if (!TextUtils.isEmpty(ip)) {
|
||||
temp = ip + ":" + port;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
public void showIPCIP() {
|
||||
ipcIp.setVisibility(View.VISIBLE);
|
||||
ipcIp.setText("IPC IP:" + getIPCIP());
|
||||
|
||||
}
|
||||
|
||||
private int getStatusColor(int connectStatus) {
|
||||
int color;
|
||||
switch (connectStatus) {
|
||||
case IPC_CONNECTION_STATUS.CONNECTED:
|
||||
color = R.color.connect_status_connected;
|
||||
break;
|
||||
default:
|
||||
case IPC_CONNECTION_STATUS.DISCONNECTED:
|
||||
color = R.color.connect_status_disconnected;
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.CONNECTING:
|
||||
color = R.color.connect_status_connecting;
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.SEARCH_ADDRESS:
|
||||
color = R.color.connect_status_search_address;
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.NOT_FOUND_ADDRESS:
|
||||
color = R.color.connect_status_disconnecting;
|
||||
break;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
private String onUpdateConnectStateView() {
|
||||
String status;
|
||||
switch (connectStatus) {
|
||||
case IPC_CONNECTION_STATUS.CONNECTED:
|
||||
status = "已连接";
|
||||
break;
|
||||
default:
|
||||
case IPC_CONNECTION_STATUS.DISCONNECTED:
|
||||
status = "未连接";
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.CONNECTING:
|
||||
status = "连接中";
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.SEARCH_ADDRESS:
|
||||
status = "搜索IP";
|
||||
break;
|
||||
case IPC_CONNECTION_STATUS.NOT_FOUND_ADDRESS:
|
||||
status = "未找到";
|
||||
break;
|
||||
}
|
||||
Message msg = Message.obtain();
|
||||
msg.obj = new IPCConnectState(status, getStatusColor(connectStatus));
|
||||
msg.what = WHAT_IPC_CONNECT_STATE;
|
||||
getHandler().sendMessage(msg);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onError(ProtocolStatus status, byte[] bytes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrajectory(MessagePad.Header header, MessagePad.Trajectory trajectory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackedObjects(MessagePad.Header header, MessagePad.TrackedObjects trackedObjects) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onGnssInfo(MessagePad.Header header, MessagePad.GnssInfo gnssInfo) {
|
||||
Message message = Message.obtain();
|
||||
message.what = WHAT_UPDATE_GNSS;
|
||||
message.obj = String.format(Locale.getDefault(), GNSS_HINT, sdf.format(new Date((long) (header.getTimestamp() * 1000))),
|
||||
gnssInfo.getLongitude(), gnssInfo.getLatitude(), gnssInfo.getAltitude(), gnssInfo.getHeading(), gnssInfo.getAcceleration(),
|
||||
gnssInfo.getYawRate(), gnssInfo.getGnssSpeed(), gnssInfo.getVehicleSpeed());
|
||||
getHandler().sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVehicleState(MessagePad.Header header, VehicleStateOuterClass.VehicleState vehicleState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutopilotState(MessagePad.Header header, MessagePad.AutopilotState autopilotState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReportMessage(MessagePad.Header header, MogoReportMsg.MogoReportMessage mogoReportMessage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerceptionTrafficLight(MessagePad.Header header, TrafficLightOuterClass.TrafficLights trafficLights) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPredictionObstacleTrajectory(MessagePad.Header header, Prediction.mPredictionObjects predictionObjects) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPointCloud(byte[] pointCloud) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlanningObjects(MessagePad.Header header, MessagePad.PlanningObjects planningObjects) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBasicInfoReq(MessagePad.Header header, MessagePad.BasicInfoReq basicInfoReq) {
|
||||
AdasManager.getInstance().sendBasicInfoResp("", 0, com.zhidao.support.adas.high.common.Constants.TERMINAL_ROLE.DEBUG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCarConfigResp(MessagePad.Header header, MessagePad.CarConfigResp carConfigResp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordResult(MessagePad.Header header, RecordPanelOuterClass.RecordPanel recordPanel) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalPathResp(MessagePad.Header header, MessagePad.GlobalPathResp globalPathResp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWarn(MessagePad.Header header, MessagePad.Warn warn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onArrivalNotification(MessagePad.Header header, MessagePad.ArrivalNotification arrivalNotification) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusQueryResp(MessagePad.Header header, SystemStatusInfo.StatusInfo statusInfo) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecordDataConfigResp(MessagePad.Header header, MessagePad.RecordDataConfig config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlanningActionMsg(MessagePad.Header header, MessagePad.PlanningActionMsg planningActionMsg) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void initAdas() {
|
||||
CupidLogUtils.e(TAG, "--->初始化");
|
||||
AdasOptions options;
|
||||
/*—————————————作为司机端———————————*/
|
||||
int mode = Constants.getIpcConnectionMode(this);
|
||||
switch (mode) {
|
||||
default:
|
||||
case AdasOptions.IPC_CONNECTION_MODE.FIXATION:
|
||||
options = new AdasOptions.Builder().setClient(false).setIpcFixationIP(AdasManager.getInstance().getIPCFixationIPList(this)).setIpcConnectionMode(mode).build();
|
||||
break;
|
||||
case AdasOptions.IPC_CONNECTION_MODE.ASSIGN:
|
||||
options = new AdasOptions.Builder().setClient(false).setIpcAssignIP(Constants.getIPCIp(this)).setIpcConnectionMode(mode).build();
|
||||
break;
|
||||
}
|
||||
AdasManager.getInstance().create(options, this);
|
||||
AdasManager.getInstance().setOnAdasListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConnectionIPCStatus(int ipcConnectionStatus, String reason) {
|
||||
// Log.i(TAG, "连接状态=" + (reason == null ? "主动断开连接" : reason));
|
||||
String time = sdf.format(new Date());
|
||||
connectStatusList.add(0, new IPCConnectState(reason == null ? "主动断开连接" : reason, getStatusColor(ipcConnectionStatus)));
|
||||
if (connectStatusList.size() > 100) {
|
||||
connectStatusList.remove(connectStatusList.size() - 1);
|
||||
}
|
||||
connectStatus = ipcConnectionStatus;
|
||||
String status = onUpdateConnectStateView();
|
||||
if (connectStatus == IPC_CONNECTION_STATUS.CONNECTED) {
|
||||
getHandler().sendEmptyMessage(WHAT_IPC_IP);
|
||||
String tem = getIPCIP();
|
||||
if (!TextUtils.isEmpty(tem)) {
|
||||
List<String> ips = Constants.getIpcUsedIps(this);
|
||||
Constants.addIpcUsedIps(this, ips, tem);
|
||||
}
|
||||
} else if (connectStatus == IPC_CONNECTION_STATUS.DISCONNECTED) {
|
||||
getHandler().sendEmptyMessage(WHAT_IPC_IP);
|
||||
}
|
||||
// LogSave.getInstance().saveLog("连接状态", status);
|
||||
// CupidLogUtils.i(TAG, "connectStatus=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompatibility(VersionCompatibility versionCompatibility) {
|
||||
showToastCenter("所连工控机:\n" + (versionCompatibility == null ? "未连接" : versionCompatibility.toString()), Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
private void showLocalIP() {
|
||||
showToastCenter("已刷新本机IP");
|
||||
localIp.setText("本机IP:" + getIpAddressString());
|
||||
}
|
||||
|
||||
private String getIpAddressString() {
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> enNetI = NetworkInterface
|
||||
.getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
|
||||
NetworkInterface netI = enNetI.nextElement();
|
||||
for (Enumeration<InetAddress> enumIpAddr = netI
|
||||
.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
|
||||
return inetAddress.getHostAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "127.0.0.1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前设备是手机还是平板,代码来自 Google I/O App for Android
|
||||
*
|
||||
* @param context
|
||||
* @return 平板返回 True,手机返回 False
|
||||
*/
|
||||
public static boolean isPad(Context context) {
|
||||
return (context.getResources().getConfiguration().screenLayout
|
||||
& Configuration.SCREENLAYOUT_SIZE_MASK)
|
||||
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onItemClick(int position, TitleBean data) {
|
||||
if (connectStatus != IPC_CONNECTION_STATUS.CONNECTED) {
|
||||
String msg = "未连接工控机";
|
||||
showToastCenter(msg);
|
||||
return;
|
||||
}
|
||||
switch (data.name) {
|
||||
case Constants.TITLE.SEND_RESTORATION:
|
||||
updateItem(Constants.TITLE.SEND_ACCELERATED_SPEED_2, -1, null);
|
||||
updateItem(Constants.TITLE.SEND_ACCELERATED_SPEED_1, -1, null);
|
||||
sendAcc(false, 0.0);
|
||||
break;
|
||||
case Constants.TITLE.SEND_CHANGE_LANE_LEFT:
|
||||
AdasManager.getInstance().sendOperatorCmdChangeLaneLeft();
|
||||
break;
|
||||
case Constants.TITLE.SEND_CHANGE_LANE_RIGHT:
|
||||
AdasManager.getInstance().sendOperatorCmdChangeLaneRight();
|
||||
break;
|
||||
case Constants.TITLE.SEND_ACCELERATED_SPEED_1:
|
||||
updateItem(Constants.TITLE.SEND_ACCELERATED_SPEED_2, position, data);
|
||||
sendAcc(true, -1);
|
||||
break;
|
||||
case Constants.TITLE.SEND_ACCELERATED_SPEED_2:
|
||||
updateItem(Constants.TITLE.SEND_ACCELERATED_SPEED_1, position, data);
|
||||
sendAcc(true, -2);
|
||||
break;
|
||||
case Constants.TITLE.SEND_HORN:
|
||||
if (timerHorn == null) {
|
||||
updateItem(null, position, data);
|
||||
AdasManager.getInstance().sendOperatorCmdStartHonking();
|
||||
timerHorn = new Timer();
|
||||
timerHorn.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
AdasManager.getInstance().sendOperatorCmdStopHonking();
|
||||
timerHorn = null;
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateItem(Constants.TITLE.SEND_HORN, -1, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, Constants.getHornDuration(this));
|
||||
}
|
||||
break;
|
||||
case Constants.TITLE.START_AUTOPILOT:
|
||||
AdasManager.getInstance().sendAutoPilotModeReq(1, 0, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateItem(String name, int position, TitleBean data) {
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
int index = titleBtnData.indexOf(new TitleBean(name));
|
||||
if (index != -1) {
|
||||
titleBtnData.get(index).isSelected = false;
|
||||
btnAdapter.setSelectedPosition(index);
|
||||
}
|
||||
}
|
||||
if (data != null) {
|
||||
data.isSelected = true;
|
||||
btnAdapter.setSelectedPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
private volatile double accelerated;//加速度
|
||||
|
||||
/**
|
||||
* 定频发送加速度
|
||||
*
|
||||
* @param isSend 是否发送
|
||||
* @param acc 加速度
|
||||
*/
|
||||
private synchronized void sendAcc(boolean isSend, double acc) {
|
||||
if (isSend) {
|
||||
accelerated = acc;
|
||||
if (timerAcc == null) {
|
||||
timerAcc = new Timer();
|
||||
timerAcc.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
AdasManager.getInstance().sendOperatorCmdSetAcceleratedSpeed(accelerated);
|
||||
}
|
||||
}, 0, Constants.getAccelerateDuration(this));
|
||||
}
|
||||
} else {
|
||||
if (timerAcc != null) {
|
||||
timerAcc.cancel();
|
||||
timerAcc = null;
|
||||
}
|
||||
AdasManager.getInstance().sendOperatorCmdSetAcceleratedSpeed(acc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(int position, TitleBean data) {
|
||||
if (Constants.TITLE.SEND_HORN.equals(data.name)) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
|
||||
builder.setTitle("鸣笛时长配置");
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_time, null);
|
||||
final EditText et = view.findViewById(R.id.et);
|
||||
String timeout = String.valueOf(Constants.getHornDuration(this));
|
||||
et.setText(timeout);
|
||||
et.setSelection(timeout.length());
|
||||
builder.setView(view);//
|
||||
builder.setPositiveButton("确定", null);
|
||||
//设置反面按钮,并做事件处理
|
||||
builder.setNegativeButton("取消", null);
|
||||
AlertDialog alertDialog = builder.show();//显示Dialog对话框
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Editable editable = et.getText();
|
||||
if (TextUtils.isEmpty(editable)) {
|
||||
// 条件不成立不能关闭 AlertDialog 窗口
|
||||
Toast.makeText(MainActivity.this, "请输入时长", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String temp = et.getText().toString().trim();
|
||||
Constants.setHornDuration(MainActivity.this, Long.parseLong(temp));
|
||||
alertDialog.dismiss();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (Constants.TITLE.SEND_ACCELERATED_SPEED_1.equals(data.name) || Constants.TITLE.SEND_ACCELERATED_SPEED_2.equals(data.name)) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
|
||||
builder.setTitle("ACC发送频率配置");
|
||||
View view = getLayoutInflater().inflate(R.layout.dialog_time, null);
|
||||
final EditText et = view.findViewById(R.id.et);
|
||||
String timeout = String.valueOf(Constants.getAccelerateDuration(this));
|
||||
et.setText(timeout);
|
||||
et.setSelection(timeout.length());
|
||||
builder.setView(view);//
|
||||
builder.setPositiveButton("确定", null);
|
||||
//设置反面按钮,并做事件处理
|
||||
builder.setNegativeButton("取消", null);
|
||||
AlertDialog alertDialog = builder.show();//显示Dialog对话框
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Editable editable = et.getText();
|
||||
if (TextUtils.isEmpty(editable)) {
|
||||
// 条件不成立不能关闭 AlertDialog 窗口
|
||||
Toast.makeText(MainActivity.this, "请输入频率", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String temp = et.getText().toString().trim();
|
||||
Constants.setAccelerateDuration(MainActivity.this, Long.parseLong(temp));
|
||||
alertDialog.dismiss();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
switch (msg.what) {
|
||||
case WHAT_IPC_IP:
|
||||
showIPCIP();
|
||||
break;
|
||||
case WHAT_IPC_CONNECT_STATE:
|
||||
if (floatWindow != null) {
|
||||
floatWindow.refreshView();
|
||||
}
|
||||
IPCConnectState status = (IPCConnectState) msg.obj;
|
||||
tvConnectState.setText(status.status);
|
||||
tvConnectState.setTextColor(getResources().getColor(status.color));
|
||||
break;
|
||||
case WHAT_UPDATE_GNSS:
|
||||
gnss_hint.setText((String) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.zhidao.adas.magic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.zhidao.adas.client.utils.PreferencesUtils;
|
||||
import com.zhidao.support.adas.high.common.JsonUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song kenan
|
||||
* @des
|
||||
* @date 2021/10/8
|
||||
*/
|
||||
public class Constants {
|
||||
public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "IPCMonitoring" + File.separator;//程序外部存储跟目录
|
||||
public static final String FILE_PATH = ROOT_PATH + "Crash" + File.separator;
|
||||
|
||||
|
||||
/***********************是否使用固定IP******************/
|
||||
// 0:固定IP 1:指定 2:UDP
|
||||
private static final String IPC_CONNECTION_MODE = "ipc_connection_mode";
|
||||
|
||||
public static void setIpcConnectionMode(Context context, int type) {
|
||||
PreferencesUtils.putInt(context, IPC_CONNECTION_MODE, type);
|
||||
}
|
||||
|
||||
public static int getIpcConnectionMode(Context context) {
|
||||
return PreferencesUtils.getInt(context, IPC_CONNECTION_MODE, 0);
|
||||
}
|
||||
|
||||
public static boolean delIpcConnectionMode(Context context) {
|
||||
return PreferencesUtils.delete(context, IPC_CONNECTION_MODE);
|
||||
}
|
||||
|
||||
/***********************保存IP******************/
|
||||
private static final String IPC_IP = "ipc_ip";
|
||||
|
||||
public static void setIPCIp(Context context, String ip) {
|
||||
PreferencesUtils.putString(context, IPC_IP, ip);
|
||||
}
|
||||
|
||||
public static String getIPCIp(Context context) {
|
||||
return PreferencesUtils.getString(context, IPC_IP, null);
|
||||
}
|
||||
|
||||
public static boolean delIPCIp(Context context) {
|
||||
return PreferencesUtils.delete(context, IPC_IP);
|
||||
}
|
||||
|
||||
|
||||
/***********************保存使用过的IP列表******************/
|
||||
|
||||
|
||||
private static final String IPC_USED_IP = "ipc_used_ip";
|
||||
|
||||
public static List<String> getIpcUsedIps(Context context) {
|
||||
String json = PreferencesUtils.getString(context, IPC_USED_IP, null);
|
||||
if (TextUtils.isEmpty(json)) return null;
|
||||
List<String> list = JsonUtil.fromJson(json, new TypeToken<List<String>>() {
|
||||
}.getType());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean addIpcUsedIps(Context context, List<String> list, String mode) {
|
||||
if (list == null)
|
||||
list = new ArrayList<>();
|
||||
list.remove(mode);
|
||||
list.add(0, mode);
|
||||
if (list.size() > 10) {
|
||||
list.remove(list.size() - 1);
|
||||
}
|
||||
return PreferencesUtils.putString(context, IPC_USED_IP, JsonUtil.toJson(list));
|
||||
}
|
||||
|
||||
public static boolean delIpcUsedIps(Context context, List<String> list, String mode) {
|
||||
if (list == null) {
|
||||
return PreferencesUtils.delete(context, IPC_USED_IP);
|
||||
} else {
|
||||
if (list.contains(mode)) {
|
||||
list.remove(mode);
|
||||
return PreferencesUtils.putString(context, IPC_USED_IP, JsonUtil.toJson(list));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************acc 发送频率******************/
|
||||
private static final String ACCELERATE_DURATION = "accelerate_duration";
|
||||
|
||||
public static void setAccelerateDuration(Context context, long duration) {
|
||||
PreferencesUtils.putLong(context, ACCELERATE_DURATION, duration);
|
||||
}
|
||||
|
||||
public static long getAccelerateDuration(Context context) {
|
||||
return PreferencesUtils.getLong(context, ACCELERATE_DURATION, 500);
|
||||
}
|
||||
|
||||
public static boolean delAccelerateDuration(Context context) {
|
||||
return PreferencesUtils.delete(context, ACCELERATE_DURATION);
|
||||
}
|
||||
/********************鸣笛时长******************/
|
||||
private static final String HORN_DURATION = "horn_duration";
|
||||
|
||||
public static void setHornDuration(Context context, long duration) {
|
||||
PreferencesUtils.putLong(context, HORN_DURATION, duration);
|
||||
}
|
||||
|
||||
public static long getHornDuration(Context context) {
|
||||
return PreferencesUtils.getLong(context, HORN_DURATION, 500);
|
||||
}
|
||||
|
||||
public static boolean delHornDuration(Context context) {
|
||||
return PreferencesUtils.delete(context, HORN_DURATION);
|
||||
}
|
||||
|
||||
public interface TITLE {
|
||||
|
||||
String SEND_RESTORATION = "复位";
|
||||
String SEND_CHANGE_LANE_LEFT = "向左变道";
|
||||
String SEND_CHANGE_LANE_RIGHT = "向右变道";
|
||||
String SEND_ACCELERATED_SPEED_1 = "-1减速";
|
||||
String SEND_ACCELERATED_SPEED_2 = "-2减速";
|
||||
String SEND_HORN = "鸣笛";
|
||||
String START_AUTOPILOT = "开启自动驾驶";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package com.zhidao.adas.magic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* ClassName: CrashHandler
|
||||
* Function: UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
|
||||
*
|
||||
* @author Norris Norris.sly@gmail.com
|
||||
* @Date 2013 2013-3-24 下午12:27:10
|
||||
* @Fields ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* @Methods ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-24下午12:27:10 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* @see ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* @since Ver 1.0 I used to be a programmer like you, then I took an arrow in the knee
|
||||
*/
|
||||
public class CrashHandler implements UncaughtExceptionHandler {
|
||||
|
||||
/**
|
||||
* Log日志的tag
|
||||
* String : TAG
|
||||
*
|
||||
* @since 2013-3-21下午8:44:28
|
||||
*/
|
||||
private static final String TAG = "NorrisInfo";
|
||||
/**
|
||||
* 系统默认的UncaughtException处理类
|
||||
* Thread.UncaughtExceptionHandler : mDefaultHandler
|
||||
*
|
||||
* @since 2013-3-21下午8:44:43
|
||||
*/
|
||||
private UncaughtExceptionHandler mDefaultHandler;
|
||||
/**
|
||||
* CrashHandler实例
|
||||
* CrashHandler : mInstance
|
||||
*
|
||||
* @since 2013-3-21下午8:44:53
|
||||
*/
|
||||
private static CrashHandler mInstance = new CrashHandler();
|
||||
/**
|
||||
* 程序的Context对象
|
||||
* Context : mContext
|
||||
*
|
||||
* @since 2013-3-21下午8:45:02
|
||||
*/
|
||||
private Context mContext;
|
||||
/**
|
||||
* 用来存储设备信息和异常信息
|
||||
* Map<String,String> : mLogInfo
|
||||
*
|
||||
* @since 2013-3-21下午8:46:15
|
||||
*/
|
||||
private Map<String, String> mLogInfo = new HashMap<String, String>();
|
||||
/**
|
||||
* 用于格式化日期,作为日志文件名的一部分(FIXME 注意在windows下文件名无法使用:等符号!)
|
||||
* SimpleDateFormat : mSimpleDateFormat
|
||||
*
|
||||
* @since 2013-3-21下午8:46:39
|
||||
*/
|
||||
private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyyMMdd_HH-mm-ss");
|
||||
|
||||
/**
|
||||
* Creates a new instance of CrashHandler.
|
||||
*/
|
||||
private CrashHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* getInstance:{获取CrashHandler实例 ,单例模式 }
|
||||
* ──────────────────────────────────
|
||||
*
|
||||
* @return CrashHandler
|
||||
* @throws
|
||||
* @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-21下午8:52:24 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
public static CrashHandler getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* init:{初始化}
|
||||
* ──────────────────────────────────
|
||||
*
|
||||
* @param paramContext
|
||||
* @return void
|
||||
* @throws
|
||||
* @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-21下午8:52:45 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
public void init(Context paramContext) {
|
||||
mContext = paramContext;
|
||||
// 获取系统默认的UncaughtException处理器
|
||||
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
// 设置该CrashHandler为程序的默认处理器
|
||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当UncaughtException发生时会转入该重写的方法来处理
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see UncaughtExceptionHandler#uncaughtException(Thread, Throwable)
|
||||
*/
|
||||
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
|
||||
// 获取设备参数信息
|
||||
getDeviceInfo(mContext);
|
||||
// mLogInfo.put("versionName", String.valueOf(BuildConfig.VERSION_CODE));
|
||||
// mLogInfo.put("versionCode", BuildConfig.VERSION_NAME);
|
||||
// 保存日志文件
|
||||
String result = saveCrashLogToFile(paramThrowable);
|
||||
mDefaultHandler.uncaughtException(paramThread, paramThrowable);
|
||||
// if (!handleException(paramThrowable) && mDefaultHandler != null) {
|
||||
// // 如果自定义的没有处理则让系统默认的异常处理器来处理
|
||||
// mDefaultHandler.uncaughtException(paramThread, paramThrowable);
|
||||
// } else {
|
||||
//// try {
|
||||
//// // 如果处理了,让程序继续运行1秒再退出,保证文件保存并上传到服务器
|
||||
//// paramThread.sleep(1000);
|
||||
//// } catch (InterruptedException e) {
|
||||
//// e.printStackTrace();
|
||||
//// }
|
||||
// // 退出程序
|
||||
// android.os.Process.killProcess(android.os.Process.myPid());
|
||||
// System.exit(1);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* handleException:{自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.}
|
||||
* ──────────────────────────────────
|
||||
*
|
||||
* @param paramThrowable
|
||||
* @return true:如果处理了该异常信息;否则返回false.
|
||||
* @throws
|
||||
* @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-24下午12:28:53 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
public boolean handleException(Throwable paramThrowable) {
|
||||
if (paramThrowable == null)
|
||||
return false;
|
||||
new Thread() {
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", Toast.LENGTH_SHORT).show();
|
||||
Looper.loop();
|
||||
}
|
||||
}.start();
|
||||
// 获取设备参数信息
|
||||
getDeviceInfo(mContext);
|
||||
// 保存日志文件
|
||||
String result = saveCrashLogToFile(paramThrowable);
|
||||
//上传崩溃日志
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getDeviceInfo:{获取设备参数信息}
|
||||
* ──────────────────────────────────
|
||||
*
|
||||
* @param paramContext
|
||||
* @throws
|
||||
* @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-24下午12:30:02 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
public void getDeviceInfo(Context paramContext) {
|
||||
try {
|
||||
// 获得包管理器
|
||||
PackageManager mPackageManager = paramContext.getPackageManager();
|
||||
// 得到该应用的信息,即主Activity
|
||||
PackageInfo mPackageInfo = mPackageManager.getPackageInfo(
|
||||
paramContext.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (mPackageInfo != null) {
|
||||
String versionName = mPackageInfo.versionName == null ? "null"
|
||||
: mPackageInfo.versionName;
|
||||
String versionCode = mPackageInfo.versionCode + "";
|
||||
mLogInfo.put("versionName", versionName);
|
||||
mLogInfo.put("versionCode", versionCode);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 反射机制
|
||||
Field[] mFields = Build.class.getDeclaredFields();
|
||||
// 迭代Build的字段key-value 此处的信息主要是为了在服务器端手机各种版本手机报错的原因
|
||||
for (Field field : mFields) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
mLogInfo.put(field.getName(), field.get("").toString());
|
||||
Log.d(TAG, field.getName() + ":" + field.get(""));
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* saveCrashLogToFile:{将崩溃的Log保存到本地}
|
||||
* TODO 可拓展,将Log上传至指定服务器路径
|
||||
* ──────────────────────────────────
|
||||
*
|
||||
* @param paramThrowable
|
||||
* @return FileName
|
||||
* @throws
|
||||
* @since I used to be a programmer like you, then I took an arrow in the knee Ver 1.0
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
* 2013-3-24下午12:31:01 Modified By Norris
|
||||
* ──────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
|
||||
|
||||
private String saveCrashLogToFile(Throwable paramThrowable) {
|
||||
StringBuffer mStringBuffer = new StringBuffer();
|
||||
for (Map.Entry<String, String> entry : mLogInfo.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
mStringBuffer.append(key + "=" + value + "\r\n");
|
||||
}
|
||||
Writer mWriter = new StringWriter();
|
||||
PrintWriter mPrintWriter = new PrintWriter(mWriter);
|
||||
paramThrowable.printStackTrace(mPrintWriter);
|
||||
paramThrowable.printStackTrace();
|
||||
Throwable mThrowable = paramThrowable.getCause();
|
||||
// 迭代栈队列把所有的异常信息写入writer中
|
||||
while (mThrowable != null) {
|
||||
mThrowable.printStackTrace(mPrintWriter);
|
||||
// 换行 每个个异常栈之间换行
|
||||
mThrowable = mThrowable.getCause();
|
||||
}
|
||||
|
||||
//记得关闭
|
||||
|
||||
String mResult = mWriter.toString();
|
||||
// String mResult = mWriter.toString().replace("\n", "").replace(":", "").replace("/", "").replace("\t", "");
|
||||
mStringBuffer.append(mResult);
|
||||
// 保存文件,设置文件名
|
||||
String mTime = mSimpleDateFormat.format(new Date());
|
||||
String mFileName = mContext.getPackageName() + "_Exception-" + mTime + ".log";
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
try {
|
||||
File mDirectory = new File(Constants.FILE_PATH);
|
||||
Log.d(TAG, mDirectory.toString());
|
||||
if (!mDirectory.exists())
|
||||
mDirectory.mkdirs();
|
||||
FileOutputStream mFileOutputStream = new FileOutputStream(mDirectory + File.separator + mFileName);
|
||||
mFileOutputStream.write(mStringBuffer.toString().getBytes());
|
||||
mFileOutputStream.close();
|
||||
return mResult;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mResult;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.zhidao.adas.magic.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class MyLinearLayoutManager extends LinearLayoutManager {
|
||||
public MyLinearLayoutManager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
|
||||
super(context, orientation, reverseLayout);
|
||||
}
|
||||
|
||||
public MyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPredictiveItemAnimations() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
//override this method and implement code as below
|
||||
try {
|
||||
super.onLayoutChildren(recycler, state);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package com.zhidao.adas.client.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class PreferencesUtils {
|
||||
|
||||
public static String PREFERENCE_NAME = "control";
|
||||
|
||||
public static boolean hasString(Context context, String key) {
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return settings.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* put string preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to modify
|
||||
* @param value The new value for the preference
|
||||
* @return True if the new values were successfully written to persistent
|
||||
* storage.
|
||||
*/
|
||||
public static boolean putString(Context context, String key, String value) {
|
||||
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putString(key, value);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* get string preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @return The preference value if it exists, or null. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a string
|
||||
* @see #getString(Context, String, String)
|
||||
*/
|
||||
public static String getString(Context context, String key) {
|
||||
if (context == null) return null;
|
||||
return getString(context, key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* get string preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @param defaultValue Value to return if this preference does not exist
|
||||
* @return The preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a string
|
||||
*/
|
||||
public static String getString(Context context, String key,
|
||||
String defaultValue) {
|
||||
|
||||
if (context == null) return defaultValue;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return settings.getString(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* put int preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to modify
|
||||
* @param value The new value for the preference
|
||||
* @return True if the new values were successfully written to persistent
|
||||
* storage.
|
||||
*/
|
||||
public static boolean putInt(Context context, String key, int value) {
|
||||
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putInt(key, value);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* get int preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @return The preference value if it exists, or -1. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a int
|
||||
* @see #getInt(Context, String, int)
|
||||
*/
|
||||
public static int getInt(Context context, String key) {
|
||||
|
||||
if (context == null) return -1;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return getInt(context, key, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get int preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @param defaultValue Value to return if this preference does not exist
|
||||
* @return The preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a int
|
||||
*/
|
||||
public static int getInt(Context context, String key, int defaultValue) {
|
||||
if (context == null) return defaultValue;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return settings.getInt(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* put long preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to modify
|
||||
* @param value The new value for the preference
|
||||
* @return True if the new values were successfully written to persistent
|
||||
* storage.
|
||||
*/
|
||||
public static boolean putLong(Context context, String key, long value) {
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putLong(key, value);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* get long preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @return The preference value if it exists, or -1. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a long
|
||||
* @see #getLong(Context, String, long)
|
||||
*/
|
||||
public static long getLong(Context context, String key) {
|
||||
if (context == null) return -1;
|
||||
return getLong(context, key, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get long preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @param defaultValue Value to return if this preference does not exist
|
||||
* @return The preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a long
|
||||
*/
|
||||
public static long getLong(Context context, String key, long defaultValue) {
|
||||
if (context == null) return defaultValue;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
return settings.getLong(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* put float preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to modify
|
||||
* @param value The new value for the preference
|
||||
* @return True if the new values were successfully written to persistent
|
||||
* storage.
|
||||
*/
|
||||
public static boolean putFloat(Context context, String key, float value) {
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putFloat(key, value);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* get float preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @return The preference value if it exists, or -1. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a float
|
||||
* @see #getFloat(Context, String, float)
|
||||
*/
|
||||
public static float getFloat(Context context, String key) {
|
||||
if (context == null) return -1;
|
||||
return getFloat(context, key, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get float preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @param defaultValue Value to return if this preference does not exist
|
||||
* @return The preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a float
|
||||
*/
|
||||
public static float getFloat(Context context, String key, float defaultValue) {
|
||||
if (context == null) return defaultValue;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return settings.getFloat(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* put boolean preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to modify
|
||||
* @param value The new value for the preference
|
||||
* @return True if the new values were successfully written to persistent
|
||||
* storage.
|
||||
*/
|
||||
public static boolean putBoolean(Context context, String key, boolean value) {
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean(key, value);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* get boolean preferences, default is false
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @return The preference value if it exists, or false. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a boolean
|
||||
* @see #getBoolean(Context, String, boolean)
|
||||
*/
|
||||
public static boolean getBoolean(Context context, String key) {
|
||||
if (context == null) return false;
|
||||
return getBoolean(context, key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* get boolean preferences
|
||||
*
|
||||
* @param context
|
||||
* @param key The name of the preference to retrieve
|
||||
* @param defaultValue Value to return if this preference does not exist
|
||||
* @return The preference value if it exists, or defValue. Throws
|
||||
* ClassCastException if there is a preference with this name that
|
||||
* is not a boolean
|
||||
*/
|
||||
public static boolean getBoolean(Context context, String key,
|
||||
boolean defaultValue) {
|
||||
if (context == null) return defaultValue;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
return settings.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
|
||||
public static boolean delete(Context context, String key) {
|
||||
if (context == null) return false;
|
||||
SharedPreferences settings = context.getSharedPreferences(
|
||||
PREFERENCE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove(key);
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FAFFFFFF" />
|
||||
<stroke
|
||||
android:width="0.8dp"
|
||||
android:color="#81666666" />
|
||||
<!-- 圆角 -->
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
19
app_mogo_magic_ring/src/main/res/drawable/btn_bg.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 圆角深红色按钮 -->
|
||||
<solid android:color="#4D0A0A" />
|
||||
<corners android:radius="8dip" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_pressed="false">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 圆角红色按钮 -->
|
||||
<solid android:color="#D9534F" />
|
||||
<corners android:radius="8dip" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
9
app_mogo_magic_ring/src/main/res/drawable/ic_pack_up.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="63.9375dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="2046"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M921.83,43.77C950.53,17.96 989.47,-6.02 1026.11,1.35c36.85,-5.52 71.62,20.29 100.28,42.42l886.1,802.25c45.01,40.56 45.01,106.96 0,147.55s-118.69,40.56 -163.7,0l-824.56,-763.52L197.46,993.55c-45.01,40.59 -118.69,40.59 -163.7,0s-45.01,-106.96 0,-147.53L921.83,43.77z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
9
app_mogo_magic_ring/src/main/res/drawable/ic_pull.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="63.9375dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="2046"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M1124.33,980.23c-28.65,25.81 -67.52,49.79 -104.37,42.42 -36.83,5.54 -71.59,-20.29 -100.24,-42.42L33.76,177.95C-11.25,137.42 -11.25,71 33.76,30.44s118.68,-40.58 163.68,0l824.49,763.46L1848.65,30.44c45,-40.58 118.66,-40.58 163.68,0s45,106.96 0,147.52L1124.33,980.23z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
69
app_mogo_magic_ring/src/main/res/drawable/icon.xml
Normal file
@@ -0,0 +1,69 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M871.68,277.12L972.8,491.52V569.6s-8.32,76.16 -78.08,95.36H108.16S57.6,668.16 44.8,646.4V516.48s-2.56,-34.56 38.4,-52.48l202.24,-49.28s123.52,-136.32 154.24,-144.64l432,7.04z"
|
||||
android:fillColor="#F8F8FF"/>
|
||||
<path
|
||||
android:pathData="M99.84,678.4c-17.92,0 -53.12,-3.2 -65.92,-25.6 -1.28,-1.92 -1.92,-4.48 -1.92,-6.4V516.48c0,-5.76 0,-44.16 46.08,-64 0.64,0 1.28,-0.64 1.92,-0.64l198.4,-48c26.24,-28.8 126.72,-136.96 157.44,-145.28 1.28,0 2.56,-0.64 3.84,-0.64l432,6.4c5.12,0 9.6,3.2 11.52,7.04L984.32,486.4c0.64,1.92 1.28,3.84 1.28,5.76V571.52c-0.64,3.2 -10.24,85.12 -87.68,106.88 -1.28,0 -2.56,0.64 -3.2,0.64H108.16c-1.28,-0.64 -4.48,-0.64 -8.32,-0.64zM57.6,642.56c8.32,8.32 33.92,11.52 49.92,10.24H892.8c56.32,-16.64 66.56,-76.8 67.2,-83.84V494.72l-96,-204.8 -421.76,-6.4c-19.2,7.68 -88.32,76.16 -146.56,140.16 -1.92,1.92 -3.84,3.2 -6.4,3.84l-201.6,49.28c-30.72,14.08 -30.08,38.4 -30.08,39.04v126.72z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M771.2,659.2m-140.16,0a140.16,140.16 0,1 0,280.32 0,140.16 140.16,0 1,0 -280.32,0Z"
|
||||
android:fillColor="#CBD6E2"/>
|
||||
<path
|
||||
android:pathData="M771.2,812.16c-84.48,0 -152.96,-68.48 -152.96,-152.96s68.48,-152.96 152.96,-152.96 152.96,68.48 152.96,152.96 -69.12,152.96 -152.96,152.96zM771.2,531.84c-70.4,0 -127.36,56.96 -127.36,127.36s56.96,127.36 127.36,127.36c70.4,0 127.36,-56.96 127.36,-127.36s-57.6,-127.36 -127.36,-127.36z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M771.2,659.2m-56.32,0a56.32,56.32 0,1 0,112.64 0,56.32 56.32,0 1,0 -112.64,0Z"
|
||||
android:fillColor="#DFECF7"/>
|
||||
<path
|
||||
android:pathData="M771.2,728.32c-38.4,0 -69.12,-30.72 -69.12,-69.12s30.72,-69.12 69.12,-69.12 69.12,30.72 69.12,69.12 -31.36,69.12 -69.12,69.12zM771.2,615.68c-23.68,0 -43.52,19.2 -43.52,43.52s19.2,43.52 43.52,43.52 43.52,-19.2 43.52,-43.52 -19.84,-43.52 -43.52,-43.52z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M305.92,667.52m-140.16,0a140.16,140.16 0,1 0,280.32 0,140.16 140.16,0 1,0 -280.32,0Z"
|
||||
android:fillColor="#CBD6E2"/>
|
||||
<path
|
||||
android:pathData="M305.92,820.48c-84.48,0 -152.96,-68.48 -152.96,-152.96s68.48,-152.96 152.96,-152.96 152.96,68.48 152.96,152.96 -69.12,152.96 -152.96,152.96zM305.92,540.16c-70.4,0 -127.36,56.96 -127.36,127.36s56.96,127.36 127.36,127.36c70.4,0 127.36,-56.96 127.36,-127.36s-57.6,-127.36 -127.36,-127.36z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M305.92,667.52m-56.32,0a56.32,56.32 0,1 0,112.64 0,56.32 56.32,0 1,0 -112.64,0Z"
|
||||
android:fillColor="#DFECF7"/>
|
||||
<path
|
||||
android:pathData="M305.92,736.64c-38.4,0 -69.12,-30.72 -69.12,-69.12s30.72,-69.12 69.12,-69.12 69.12,30.72 69.12,69.12 -31.36,69.12 -69.12,69.12zM305.92,624c-23.68,0 -43.52,19.2 -43.52,43.52s19.2,43.52 43.52,43.52 43.52,-19.2 43.52,-43.52 -19.84,-43.52 -43.52,-43.52z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M117.76,464s5.76,64 -65.92,96.64l1.92,-55.68s2.56,-28.8 32.64,-39.04l31.36,-8.32v6.4z"
|
||||
android:fillColor="#FEF185"/>
|
||||
<path
|
||||
android:pathData="M38.4,580.48l2.56,-76.8c0,-1.28 3.2,-37.12 40.96,-49.92h0.64L128,441.6l2.56,21.12c0,3.2 5.76,73.6 -73.6,109.44L38.4,580.48zM65.92,505.6l-0.64,32.64c31.36,-21.76 38.4,-49.92 39.68,-64.64l-15.36,4.48c-20.48,7.04 -23.04,25.6 -23.68,27.52z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M850.56,309.12l55.04,131.2 -568.96,-9.6 122.24,-128.64z"
|
||||
android:fillColor="#D0E5FE"/>
|
||||
<path
|
||||
android:pathData="M905.6,454.4l-569.6,-10.24c-5.12,0 -9.6,-3.2 -11.52,-8.32s-1.28,-10.24 2.56,-14.08l122.24,-128.64c2.56,-2.56 5.76,-4.48 9.6,-3.84l391.68,6.4c5.12,0 9.6,3.2 11.52,7.68l55.68,131.2c1.92,3.84 1.28,8.96 -1.28,12.8 -2.56,4.48 -6.4,7.04 -10.88,7.04zM366.08,418.56l520.32,8.32 -44.8,-105.6 -377.6,-6.4 -97.92,103.68z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M582.4,300.8v147.2"
|
||||
android:fillColor="#F8F8FF"/>
|
||||
<path
|
||||
android:pathData="M569.6,300.8h25.6v147.2h-25.6z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M246.4,355.2h-0.64c-7.04,-0.64 -12.16,-6.4 -12.16,-13.44 1.92,-31.36 27.52,-42.88 44.16,-42.88 7.04,0 12.8,5.76 12.8,12.8s-5.76,12.8 -12.8,12.8c-3.84,0 -17.28,1.92 -18.56,19.2 0,6.4 -5.76,11.52 -12.8,11.52z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M197.76,336.64h-0.64c-7.04,-0.64 -12.16,-6.4 -12.16,-13.44 3.2,-51.84 44.8,-65.92 66.56,-65.92 7.04,0 12.8,5.76 12.8,12.8s-5.76,12.8 -12.8,12.8c-3.84,0 -38.4,1.92 -40.96,41.6 -0.64,6.4 -6.4,12.16 -12.8,12.16z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M136.32,322.56h-0.64c-7.04,-0.64 -12.16,-6.4 -12.16,-13.44 4.48,-83.2 71.04,-104.96 104.96,-104.96 7.04,0 12.8,5.76 12.8,12.8s-5.76,12.8 -12.8,12.8c-3.2,0 -74.88,1.28 -80,80.64 0,7.04 -5.76,12.16 -12.16,12.16z"
|
||||
android:fillColor="#708DB7"/>
|
||||
<path
|
||||
android:pathData="M524.8,486.4h44.8"
|
||||
android:fillColor="#F8F8FF"/>
|
||||
<path
|
||||
android:pathData="M569.6,499.2h-44.8c-7.04,0 -12.8,-5.76 -12.8,-12.8s5.76,-12.8 12.8,-12.8h44.8c7.04,0 12.8,5.76 12.8,12.8s-5.76,12.8 -12.8,12.8z"
|
||||
android:fillColor="#708DB7"/>
|
||||
</vector>
|
||||
31
app_mogo_magic_ring/src/main/res/drawable/item_bg.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 圆角红色按钮 -->
|
||||
<solid android:color="#00BFFF" />
|
||||
<corners android:radius="8dp" />
|
||||
<!--边框填充色,边框宽度-->
|
||||
<stroke android:width="1dp" android:color="#B0E0E6" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 圆角深红色按钮 -->
|
||||
<solid android:color="#808080" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_pressed="false">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 圆角红色按钮 -->
|
||||
<solid android:color="#C0C0C0" />
|
||||
<corners android:radius="8dp" />
|
||||
<!--边框填充色,边框宽度-->
|
||||
<stroke android:width="1dp" android:color="#808080" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#7CFC00" android:state_pressed="true" />
|
||||
<item android:color="#7CFC00" android:state_checked="true" />
|
||||
<item android:color="#8B4513" android:state_selected="true" />
|
||||
<item android:color="#FFFFFF" />
|
||||
</selector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_pack_up" android:state_selected="true" />
|
||||
<item android:drawable="@drawable/ic_pull" />
|
||||
</selector>
|
||||
39
app_mogo_magic_ring/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?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:background="#F5F5F5"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.MainActivity">
|
||||
|
||||
<include
|
||||
android:id="@+id/include_title"
|
||||
layout="@layout/item_main" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hint"
|
||||
android:textColor="#666666"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gnss_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="60dp"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/info_btn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="3dp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
29
app_mogo_magic_ring/src/main/res/layout/dialog_time.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:digits="0123456789"
|
||||
android:gravity="center"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:inputType="number"
|
||||
android:maxLength="10"
|
||||
android:maxLines="1"
|
||||
android:minWidth="100dp"
|
||||
android:textColor="#000"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:text="ms"
|
||||
android:textColor="#000"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
14
app_mogo_magic_ring/src/main/res/layout/include_line.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp">
|
||||
|
||||
<View
|
||||
android:layout_width="2dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorSlateGray"/>
|
||||
|
||||
</LinearLayout>
|
||||
21
app_mogo_magic_ring/src/main/res/layout/item_info.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="4dp"
|
||||
android:background="@drawable/item_bg"
|
||||
android:gravity="center"
|
||||
android:minHeight="100dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_info_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:text="INFO"
|
||||
android:textColor="@drawable/item_text_color"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
177
app_mogo_magic_ring/src/main/res/layout/item_main.xml
Normal file
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="55dp"
|
||||
android:background="@color/colorBlue2"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_ip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_connect_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="5dp"
|
||||
android:gravity="center"
|
||||
android:layoutDirection="ltr"
|
||||
android:text="未连接"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ipc_ip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="IPC IP:"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="10dp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_ip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="本机IP:"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="10dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_toEndOf="@id/layout_ip"
|
||||
android:layoutDirection="rtl">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/disconnect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/btn_bg"
|
||||
android:text="断开"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@drawable/btn_bg"
|
||||
android:text="连接"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:id="@+id/line"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_toStartOf="@+id/connect"
|
||||
android:background="#fff124" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/connection_type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/assign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="指定"
|
||||
android:textColor="#ffffff" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/fixation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="固定"
|
||||
android:textColor="#ffffff" />
|
||||
</RadioGroup>
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_ip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:digits="0123456789.::"
|
||||
android:gravity="right|center_vertical"
|
||||
android:hint="输入指定IP:PORT"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
android:inputType="number"
|
||||
android:layoutDirection="ltr"
|
||||
android:maxLength="21"
|
||||
android:maxLines="1"
|
||||
android:minWidth="206dp"
|
||||
android:paddingStart="40dp"
|
||||
android:textColor="@color/colorWhile"
|
||||
android:textColorHint="#DEDEDE"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/tv_ip"
|
||||
android:layout_width="26dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="-42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/selector_history_ip" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
</RelativeLayout>
|
||||
12
app_mogo_magic_ring/src/main/res/layout/item_pop.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
android:textColor="@color/colorBlock"
|
||||
android:textSize="16sp" />
|
||||
31
app_mogo_magic_ring/src/main/res/layout/item_status.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--测试列表Item-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#999999"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/log"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:cursorVisible="false"
|
||||
android:focusable="false"
|
||||
android:textColor="#ff0000"
|
||||
android:textSize="12sp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
17
app_mogo_magic_ring/src/main/res/layout/layout_float.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_adas_dialog"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_status"
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="280dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
20
app_mogo_magic_ring/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
<color name="colorGold">#FFD700</color>
|
||||
<color name="colorBlue">#102b6a</color>
|
||||
<color name="colorBlue2">#224b8f</color>
|
||||
<color name="colorWhile">#FFFFFF</color>
|
||||
<color name="colorBlock">#000000</color>
|
||||
<color name="colorDeepPink">#FF1493</color>
|
||||
<color name="colorCrimson">#DC143C</color>
|
||||
<color name="colorLimeGreen">#32CD32</color>
|
||||
<color name="colorSlateGray">#708090</color>
|
||||
<color name="connect_status_connected">#32CD32</color>
|
||||
<color name="connect_status_disconnected">#DC143C</color>
|
||||
<color name="connect_status_connecting">#FF00FF</color>
|
||||
<color name="connect_status_disconnecting">#DAA520</color>
|
||||
<color name="connect_status_search_address">#1E90FF</color>
|
||||
</resources>
|
||||
5
app_mogo_magic_ring/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">蘑戒</string>
|
||||
<string name="hint">复位按钮:下发减速命令之后需要复位操作\n减速按钮:单机触发减速,长按配置减速发送频率\n鸣笛按钮:单机触发鸣笛,长按配置鸣笛时长</string>
|
||||
|
||||
</resources>
|
||||
34
app_mogo_magic_ring/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">#224b8f</item>
|
||||
<item name="colorPrimaryDark">@color/colorBlue</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/white</item>
|
||||
<item name="android:windowEnableSplitTouch">false</item>
|
||||
<item name="android:splitMotionEvents">false</item>
|
||||
</style>
|
||||
<style name="CustomDialog" parent="Theme.MaterialComponents.Light.Dialog">
|
||||
<!--背景颜色及和透明程度-->
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<!--是否去除标题 -->
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<!--是否去除边框-->
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<!--是否浮现在activity之上-->
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<!--是否模糊-->
|
||||
<item name="android:backgroundDimEnabled">true</item>
|
||||
</style>
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true" />
|
||||
</network-security-config>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.zhidao.adas.magic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,11 @@ ext {
|
||||
//========================= DataStore ======================
|
||||
androidx_datastore_preferences : "androidx.datastore:datastore-preferences:1.0.0",
|
||||
androidx_datastore : "androidx.datastore:datastore:1.0.0",
|
||||
android_start_up : "com.mogo:android-startup:1.0.1"
|
||||
android_start_up : "com.mogo:android-startup:1.0.1",
|
||||
|
||||
//========================= Shizuku ======================
|
||||
shizuku_api : "dev.rikka.shizuku:api:12.1.0",
|
||||
shizuku_provider : "dev.rikka.shizuku:provider:12.1.0"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -428,6 +428,38 @@ class MoGoAutopilotProvider :
|
||||
AdasManager.getInstance().sendRecordDataConfigReq()
|
||||
}
|
||||
|
||||
/**
|
||||
* 向左变道
|
||||
*/
|
||||
override fun sendOperatorChangeLaneLeft() {
|
||||
// Log.d("liyz", "MoGoAutopilotProvider sendOperatorChangeLaneLeft -------> ")
|
||||
// AdasManager.getInstance().sendOperatorCmdChangeLaneLeft()
|
||||
}
|
||||
|
||||
/**
|
||||
* 向右变道
|
||||
*/
|
||||
override fun sendOperatorChangeLaneRight() {
|
||||
Log.d("liyz", "MoGoAutopilotProvider sendOperatorChangeLaneRight -------> ")
|
||||
// AdasManager.getInstance().sendOperatorCmdChangeLaneRight()
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送设置加速度 acc>0加速 acc<0减速 acc=0复位
|
||||
*/
|
||||
override fun sendOperatorSetAcceleratedSpeed(cc: Double) {
|
||||
// Log.d("liyz", "MoGoAutopilotProvider sendOperatorSetAcceleratedSpeed cc $cc ")
|
||||
// AdasManager.getInstance().sendOperatorCmdSetAcceleratedSpeed(cc)
|
||||
}
|
||||
|
||||
/**
|
||||
* 鸣笛 value 1: honk 2: stop honking
|
||||
*/
|
||||
override fun sendOperatorSetHorn(value: Double) {
|
||||
// Log.d("liyz", "MoGoAutopilotProvider sendOperatorSetHorn value $value ")
|
||||
// AdasManager.getInstance().sendOperatorCmdSetHorn(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送工控机所有节点重启命令
|
||||
*/
|
||||
|
||||
@@ -82,7 +82,6 @@ dependencies {
|
||||
implementation rootProject.ext.dependencies.androidxconstraintlayout
|
||||
implementation rootProject.ext.dependencies.androidxrecyclerview
|
||||
implementation rootProject.ext.dependencies.flexbox
|
||||
|
||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||
implementation rootProject.ext.dependencies.mogoserviceapi
|
||||
implementation rootProject.ext.dependencies.modulecommon
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.mogo.eagle.core.data.deva.scene.SceneTAG
|
||||
import com.mogo.eagle.core.function.api.devatools.IDevaToolsProvider
|
||||
import com.zhjt.mogo_core_function_devatools.badcase.BadCaseManager
|
||||
import com.zhjt.mogo_core_function_devatools.logcatch.MogoLogCatchManager
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.MonitorManager
|
||||
import com.zhjt.mogo_core_function_devatools.scene.SceneManager.Companion.sceneManager
|
||||
import com.zhjt.mogo_core_function_devatools.status.*
|
||||
import com.zhjt.mogo_core_function_devatools.trace.TraceManager.Companion.traceManager
|
||||
@@ -110,4 +111,12 @@ class DevaToolsProvider : IDevaToolsProvider {
|
||||
override fun hideStatusBar() {
|
||||
StatusManager.hide()
|
||||
}
|
||||
|
||||
override fun startMonitor() {
|
||||
mContext?.let { MonitorManager.getInstance(it)?.startMonitor() }
|
||||
}
|
||||
|
||||
override fun stopMonitor() {
|
||||
mContext?.let { MonitorManager.getInstance(it)?.stopMonitor() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Process
|
||||
import android.util.Log
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.CpuInfo
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.utils.CpuUtils
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
class CpuMonitor private constructor(var context: Context) {
|
||||
|
||||
var isRunning = false
|
||||
|
||||
private val pid by lazy {
|
||||
Process.myPid()
|
||||
}
|
||||
|
||||
private val cpuInfoList by lazy {
|
||||
ArrayList<CpuInfo>()
|
||||
}
|
||||
|
||||
private val dateFormat by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CpuMonitor"
|
||||
const val INTERVAL_PERF = 1000L
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Volatile
|
||||
private var sInstance: CpuMonitor? = null
|
||||
fun getInstance(context: Context): CpuMonitor? {
|
||||
if (sInstance == null) {
|
||||
synchronized(CpuMonitor::class.java) {
|
||||
if (sInstance == null) {
|
||||
sInstance = CpuMonitor(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请先运行Shizuku的服务
|
||||
*/
|
||||
@Synchronized
|
||||
fun startRun() {
|
||||
if (!isRunning) {
|
||||
Thread(CpuRunnable()).start()
|
||||
isRunning = true
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun isStarted() = isRunning
|
||||
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
fun saveCpuInfos() {
|
||||
synchronized(this) {
|
||||
MonitorDb.getDb(context).monitorDao().saveAllCpuInfos(*cpuInfoList.toTypedArray())
|
||||
cpuInfoList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCpuValue(): Double {
|
||||
var value = 0.0
|
||||
try {
|
||||
// 应用获取当前进程CPU使用时间,Binder服务端获取系统CPU时间,中间用耗时,不如使用top命令
|
||||
// value = UserServiceManager.getCpuUsage(Process.myPid())
|
||||
value = CpuUtils.getCpuUsageForO()
|
||||
return value
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.message ?: "未知异常!")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
inner class CpuRunnable : Runnable {
|
||||
override fun run() {
|
||||
while (isRunning) {
|
||||
val cpu = getCpuValue()
|
||||
if (cpu > 0) {
|
||||
synchronized(this@CpuMonitor) {
|
||||
cpuInfoList.add(
|
||||
CpuInfo(
|
||||
saveTime = dateFormat.format(System.currentTimeMillis()),
|
||||
cpuPercent = cpu
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(INTERVAL_PERF)
|
||||
} catch (e: InterruptedException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Process.myPid
|
||||
import android.util.Log
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.utils.HookUtils
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.utils.MemUtils
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
class MemMonitor private constructor(var context: Context) {
|
||||
|
||||
var isRunning = false
|
||||
|
||||
private val memInfoList by lazy {
|
||||
ArrayList<MemInfo>()
|
||||
}
|
||||
|
||||
private val dateFormat by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MemMonitor"
|
||||
const val INTERVAL_PERF = 1000L
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Volatile
|
||||
private var sInstance: MemMonitor? = null
|
||||
fun getInstance(context: Context): MemMonitor? {
|
||||
if (sInstance == null) {
|
||||
synchronized(MemMonitor::class.java) {
|
||||
if (sInstance == null) {
|
||||
sInstance = MemMonitor(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun startRun() {
|
||||
if (!isRunning) {
|
||||
Thread(MemoryRunnable()).start()
|
||||
isRunning = true
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun isStarted() = isRunning
|
||||
|
||||
@Synchronized
|
||||
fun stop() {
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
fun saveMemInfos() {
|
||||
synchronized(this) {
|
||||
MonitorDb.getDb(context).monitorDao().saveAllMemInfos(*memInfoList.toTypedArray())
|
||||
memInfoList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单位MB
|
||||
*/
|
||||
private fun getMemoryValue(): Double {
|
||||
var value = 0.0
|
||||
try {
|
||||
value = MemUtils.getPssMemValue(context, intArrayOf(myPid()))
|
||||
return value
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.message ?: "未知异常!")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* 单位MB
|
||||
*/
|
||||
private fun getMemoryArray(): DoubleArray {
|
||||
var value = doubleArrayOf()
|
||||
try {
|
||||
value = MemUtils.getPssArray(context, intArrayOf(myPid()))
|
||||
return value
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.message ?: "未知异常!")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
inner class MemoryRunnable : Runnable {
|
||||
override fun run() {
|
||||
while (isRunning) {
|
||||
// 0: 总PSS,1: dalvik总Pss, 2: native总Pss, 3: other总Pss
|
||||
val pssArray = getMemoryArray()
|
||||
if (pssArray[0] > 0) {
|
||||
synchronized(this@MemMonitor) {
|
||||
memInfoList.add(
|
||||
MemInfo(
|
||||
saveTime = dateFormat.format(System.currentTimeMillis()),
|
||||
totalPss = pssArray[0],
|
||||
dalvikPss = pssArray[1],
|
||||
nativePss = pssArray[2],
|
||||
otherPss = pssArray[3]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(INTERVAL_PERF)
|
||||
} catch (e: InterruptedException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
import java.util.*
|
||||
|
||||
class MonitorManager private constructor(var context: Context) {
|
||||
|
||||
private var timer: Timer? = null
|
||||
|
||||
private var isStarted = false
|
||||
|
||||
companion object {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Volatile
|
||||
private var sInstance: MonitorManager? = null
|
||||
fun getInstance(context: Context): MonitorManager? {
|
||||
if (sInstance == null) {
|
||||
synchronized(MonitorManager::class.java) {
|
||||
if (sInstance == null) {
|
||||
sInstance = MonitorManager(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主线程中执行
|
||||
*/
|
||||
fun startMonitor() {
|
||||
if (Thread.currentThread() == Looper.getMainLooper().thread) {
|
||||
if (!isStarted) {
|
||||
CpuMonitor.getInstance(context)?.startRun()
|
||||
MemMonitor.getInstance(context)?.startRun()
|
||||
if (timer == null) {
|
||||
timer = Timer()
|
||||
}
|
||||
timer!!.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
CpuMonitor.getInstance(context)?.saveCpuInfos()
|
||||
MemMonitor.getInstance(context)?.saveMemInfos()
|
||||
}
|
||||
}, 1000, 60000)
|
||||
isStarted = true
|
||||
ToastUtils.showShort("性能监控启动成功!")
|
||||
} else {
|
||||
ToastUtils.showShort("性能监控已启动,请勿重复启动!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopMonitor() {
|
||||
CpuMonitor.getInstance(context)?.stop()
|
||||
MemMonitor.getInstance(context)?.stop()
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
isStarted = false
|
||||
ToastUtils.showShort("性能监控停止成功!")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "t_cpu")
|
||||
data class CpuInfo(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id")
|
||||
val uuid: Long = 0,
|
||||
|
||||
@ColumnInfo(name = "save_time")
|
||||
val saveTime: String?,
|
||||
|
||||
@ColumnInfo(name = "cpu_percent")
|
||||
val cpuPercent: Double
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "t_memory")
|
||||
data class MemInfo(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id")
|
||||
val uuid: Long = 0,
|
||||
|
||||
@ColumnInfo(name = "save_time")
|
||||
val saveTime: String,
|
||||
|
||||
@ColumnInfo(name = "total_pss")
|
||||
val totalPss: Double,
|
||||
|
||||
@ColumnInfo(name = "dalvik_pss")
|
||||
val dalvikPss: Double,
|
||||
|
||||
@ColumnInfo(name = "native_pss")
|
||||
val nativePss: Double,
|
||||
|
||||
@ColumnInfo(name = "other_pss")
|
||||
val otherPss: Double
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.db
|
||||
|
||||
import androidx.room.*
|
||||
|
||||
@Dao
|
||||
interface MonitorDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveCpu(info: CpuInfo)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveAllCpuInfos(vararg cpuInfo: CpuInfo)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveMemory(info: MemInfo)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveAllMemInfos(vararg memInfo: MemInfo)
|
||||
|
||||
@Query("SELECT * FROM t_cpu WHERE id =:id")
|
||||
fun getAllCPUById(id: Long): List<CpuInfo>
|
||||
|
||||
@Query("SELECT * FROM t_memory WHERE id =:id")
|
||||
fun getAllMemById(id: Long): List<MemInfo>
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(entities = [CpuInfo::class, MemInfo::class], version = 1, exportSchema = false)
|
||||
abstract class MonitorDb: RoomDatabase() {
|
||||
abstract fun monitorDao(): MonitorDao
|
||||
|
||||
companion object {
|
||||
const val INTERNAL_DB_NAME = "mogo_monitor.db"
|
||||
|
||||
private var db: MonitorDb? = null
|
||||
|
||||
@JvmStatic
|
||||
fun getDb(context: Context): MonitorDb {
|
||||
if (db == null) {
|
||||
db = Room.databaseBuilder(context.applicationContext, MonitorDb::class.java, INTERNAL_DB_NAME)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
}
|
||||
return db!!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.remote;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.RemoteException;
|
||||
|
||||
public interface IUserInterface extends IInterface {
|
||||
|
||||
String DESCRIPTOR = "rikka.shizuku.demo.IUserInterface";
|
||||
|
||||
int TRANSACTION_exec = IBinder.FIRST_CALL_TRANSACTION;
|
||||
int TRANSACTION_getCpuUsage = IBinder.FIRST_CALL_TRANSACTION + 1;
|
||||
|
||||
String exec(String cmd) throws RemoteException;
|
||||
|
||||
double getCpuUsage(int pid) throws RemoteException;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.remote;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.utils.CpuUtils;
|
||||
|
||||
/**
|
||||
* Server端Binder类
|
||||
*/
|
||||
public class RemoteUserServiceImp extends Binder implements IUserInterface {
|
||||
|
||||
public RemoteUserServiceImp() {
|
||||
attachInterface(this, DESCRIPTOR);
|
||||
}
|
||||
|
||||
public static IUserInterface asInterface(IBinder obj) {
|
||||
if ((obj == null)) {
|
||||
return null;
|
||||
}
|
||||
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
|
||||
if (iin instanceof IUserInterface) {
|
||||
return ((IUserInterface) iin);
|
||||
}
|
||||
return new Proxy(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String exec(String cmd) throws RemoteException {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCpuUsage(int pid) throws RemoteException {
|
||||
return CpuUtils.getCpuUsagePercentWithPid(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Client端请求
|
||||
*
|
||||
* @param code
|
||||
* @param data
|
||||
* @param reply
|
||||
* @param flags
|
||||
* @return
|
||||
* @throws RemoteException
|
||||
*/
|
||||
@Override
|
||||
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
|
||||
final String descriptor = DESCRIPTOR;
|
||||
switch (code) {
|
||||
case TRANSACTION_exec:
|
||||
data.enforceInterface(descriptor);
|
||||
String _arg0;
|
||||
_arg0 = data.readString();
|
||||
String _result = this.exec(_arg0);
|
||||
reply.writeNoException();
|
||||
reply.writeString(_result);
|
||||
return true;
|
||||
default:
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getInterfaceDescriptor() {
|
||||
return super.getInterfaceDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回给Client端并被封装成BinderProxy
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static class Proxy implements IUserInterface {
|
||||
private IBinder mRemote;
|
||||
|
||||
public Proxy(IBinder remote) {
|
||||
mRemote = remote;
|
||||
}
|
||||
|
||||
public String getInterfaceDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
// 返回Server端Binder对象
|
||||
return mRemote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String exec(String cmd) throws RemoteException {
|
||||
// 参数传递给Server端并挂起当前线程,等待Server端执行完返回结果
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
String _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeString(cmd);
|
||||
mRemote.transact(TRANSACTION_exec, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readString();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCpuUsage(int pid) throws RemoteException {
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
double _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(pid);
|
||||
mRemote.transact(TRANSACTION_getCpuUsage, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
_result = _reply.readDouble();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.remote
|
||||
|
||||
//import rikka.shizuku.Shizuku
|
||||
//import android.content.ServiceConnection
|
||||
//import android.content.ComponentName
|
||||
//import android.os.IBinder
|
||||
//import rikka.shizuku.Shizuku.UserServiceArgs
|
||||
//import android.content.pm.PackageManager
|
||||
//import android.util.Log
|
||||
//import com.mogo.eagle.core.utilcode.util.AppUtils
|
||||
//import com.mogo.eagle.core.utilcode.util.ToastUtils
|
||||
//import rikka.shizuku.Shizuku.OnBinderReceivedListener
|
||||
//import rikka.shizuku.Shizuku.OnBinderDeadListener
|
||||
//import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
||||
//import java.lang.StringBuilder
|
||||
|
||||
object UserServiceManager {
|
||||
|
||||
// private const val TAG = "UserServiceManager"
|
||||
// private const val REQUEST_CODE_BIND = 1
|
||||
// private const val REQUEST_CODE_UNBIND = 2
|
||||
//
|
||||
// private var userServiceProxy: IUserInterface? = null
|
||||
// private var isFirst = true
|
||||
//
|
||||
// fun init() {
|
||||
// if (isFirst) {
|
||||
// addListener()
|
||||
// isFirst = false
|
||||
// }
|
||||
// bindUserService()
|
||||
// }
|
||||
//
|
||||
// fun stopService() {
|
||||
// removeListener()
|
||||
// unbindUserService()
|
||||
// }
|
||||
//
|
||||
// fun isServiceActive(): Boolean {
|
||||
// var isActive = false
|
||||
// val service = userServiceProxy
|
||||
// if (service != null && service is IBinder && service.isBinderAlive) {
|
||||
// isActive = true
|
||||
// }
|
||||
// return isActive
|
||||
// }
|
||||
//
|
||||
// private fun addListener() {
|
||||
// Shizuku.addBinderReceivedListenerSticky(BINDER_RECEIVED_LISTENER)
|
||||
// Shizuku.addBinderDeadListener(BINDER_DEAD_LISTENER)
|
||||
// Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
|
||||
// }
|
||||
//
|
||||
// private fun removeListener() {
|
||||
// Shizuku.removeBinderReceivedListener(BINDER_RECEIVED_LISTENER)
|
||||
// Shizuku.removeBinderDeadListener(BINDER_DEAD_LISTENER)
|
||||
// Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
|
||||
// }
|
||||
//
|
||||
// private fun bindUserService() {
|
||||
// if (checkPermission(REQUEST_CODE_BIND)) {
|
||||
// val res = StringBuilder()
|
||||
// try {
|
||||
// if (Shizuku.getVersion() < 10) {
|
||||
// res.append("requires Shizuku API 10")
|
||||
// } else {
|
||||
// Shizuku.bindUserService(userServiceArgs, userServiceConnection)
|
||||
// }
|
||||
// } catch (tr: Throwable) {
|
||||
// tr.printStackTrace()
|
||||
// res.append(tr.toString())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun unbindUserService() {
|
||||
// if (checkPermission(REQUEST_CODE_UNBIND)) {
|
||||
// val res = StringBuilder()
|
||||
// try {
|
||||
// if (Shizuku.getVersion() < 10) {
|
||||
// res.append("requires Shizuku API 10")
|
||||
// } else {
|
||||
// Shizuku.unbindUserService(userServiceArgs, userServiceConnection, true)
|
||||
// }
|
||||
// } catch (tr: Throwable) {
|
||||
// tr.printStackTrace()
|
||||
// res.append(tr.toString())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun exec(cmd: String): String {
|
||||
// return userServiceProxy?.exec(cmd) ?: ""
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 单位:%,保留1位小数
|
||||
// */
|
||||
// fun getCpuUsage(pid: Int): Double {
|
||||
// return userServiceProxy?.getCpuUsage(pid) ?: -1.0
|
||||
// }
|
||||
//
|
||||
// private val userServiceConnection: ServiceConnection = object : ServiceConnection {
|
||||
// override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {
|
||||
// if (binder != null && binder.pingBinder()) {
|
||||
// val service = RemoteUserServiceImp.asInterface(binder)
|
||||
// userServiceProxy = service
|
||||
// ToastUtils.showShort("监控权限获取成功!")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onServiceDisconnected(componentName: ComponentName) {}
|
||||
// }
|
||||
//
|
||||
// private val userServiceArgs by lazy {
|
||||
// val appInfo = AppUtils.getAppInfo()
|
||||
// UserServiceArgs(
|
||||
// ComponentName(
|
||||
// appInfo?.packageName ?: "com.mogo.launcher.f",
|
||||
// RemoteUserServiceImp::class.java.name
|
||||
// )
|
||||
// )
|
||||
// .daemon(false)
|
||||
// .processNameSuffix("service")
|
||||
// .debuggable(true)
|
||||
// .version(appInfo?.versionCode ?: 1)
|
||||
// }
|
||||
//
|
||||
// private fun checkPermission(code: Int): Boolean {
|
||||
// if (Shizuku.isPreV11()) {
|
||||
// Log.w(TAG, "Version is preV11!")
|
||||
// return false
|
||||
// }
|
||||
// try {
|
||||
// return if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||
// true
|
||||
// } else if (Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
// Log.e(TAG, "User denied permission (shouldShowRequestPermissionRationale=true)")
|
||||
// false
|
||||
// } else {
|
||||
// Shizuku.requestPermission(code)
|
||||
// false
|
||||
// }
|
||||
// } catch (e: Throwable) {
|
||||
// Log.e(TAG, Log.getStackTraceString(e))
|
||||
// ToastUtils.showLong("请先打开Shizuku并启动它!")
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// private fun onRequestPermissionsResult(requestCode: Int, grantResult: Int) {
|
||||
// if (grantResult == PackageManager.PERMISSION_GRANTED) {
|
||||
// when (requestCode) {
|
||||
// REQUEST_CODE_BIND -> {
|
||||
// bindUserService()
|
||||
// }
|
||||
// REQUEST_CODE_UNBIND -> {
|
||||
// unbindUserService()
|
||||
// }
|
||||
// else -> {}
|
||||
// }
|
||||
// } else {
|
||||
// Log.e(TAG, "User denied permission")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private val BINDER_RECEIVED_LISTENER = OnBinderReceivedListener {
|
||||
// if (Shizuku.isPreV11()) {
|
||||
// Log.w(TAG, "Shizuku pre-v11 is not supported")
|
||||
// } else {
|
||||
// Log.d(TAG, "Binder received")
|
||||
// }
|
||||
// }
|
||||
// private val BINDER_DEAD_LISTENER = OnBinderDeadListener { Log.w(TAG, "Binder dead") }
|
||||
// private val REQUEST_PERMISSION_RESULT_LISTENER =
|
||||
// OnRequestPermissionResultListener { requestCode: Int, grantResult: Int ->
|
||||
// onRequestPermissionsResult(
|
||||
// requestCode,
|
||||
// grantResult
|
||||
// )
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.utils;
|
||||
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* CPU相关工具类。
|
||||
*/
|
||||
public class CpuUtils {
|
||||
private static boolean initCpu = true;
|
||||
private static double oCpu = 0.0;
|
||||
private static double oIdle = 0.0;
|
||||
|
||||
private static double pJif = 0.0;
|
||||
private static double pCpu = 0.0;
|
||||
private static double aCpu = 0.0;
|
||||
private static double opCpu = 0.0;
|
||||
private static double oaCpu = 0.0;
|
||||
|
||||
/**
|
||||
* 8.0以上获取cpu的方式
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static double getCpuUsageForO() {
|
||||
java.lang.Process process = null;
|
||||
try {
|
||||
process = Runtime.getRuntime().exec("top -n 1");
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line;
|
||||
int cpuIndex = -1;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (TextUtils.isEmpty(line)) {
|
||||
continue;
|
||||
}
|
||||
int tempIndex = getCPUIndex(line);
|
||||
if (tempIndex != -1) {
|
||||
cpuIndex = tempIndex;
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(String.valueOf(Process.myPid()))) {
|
||||
if (cpuIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
String[] param = line.split("\\s+");
|
||||
if (param.length <= cpuIndex) {
|
||||
continue;
|
||||
}
|
||||
String cpu = param[cpuIndex];
|
||||
if (cpu.endsWith("%")) {
|
||||
cpu = cpu.substring(0, cpu.lastIndexOf("%"));
|
||||
}
|
||||
return Double.parseDouble(cpu) / Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int getCPUIndex(String line) {
|
||||
if (line.contains("CPU")) {
|
||||
String[] titles = line.split("\\s+");
|
||||
for (int i = 0; i < titles.length; i++) {
|
||||
if (titles[i].contains("CPU")) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用的CPU占用率,单位:%,保留1位小数
|
||||
* @param pid
|
||||
* @return
|
||||
*/
|
||||
public static double getCpuUsagePercentWithPid(int pid) {
|
||||
double usage = 0.0;
|
||||
String[] result1;
|
||||
String[] result2;
|
||||
if (pid >= 0) {
|
||||
result1 = getCpuWithPid(pid);
|
||||
if (null != result1) {
|
||||
pCpu = Double.parseDouble(result1[1])
|
||||
+ Double.parseDouble(result1[2]);
|
||||
}
|
||||
result2 = getSystemCpu();
|
||||
if (null != result2) {
|
||||
aCpu = 0.0;
|
||||
for (int i = 2; i < result2.length; i++) {
|
||||
if (result2[i] != null) {
|
||||
aCpu += Double.parseDouble(result2[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((aCpu - oaCpu) != 0) {
|
||||
usage = DoubleUtils.div(((pCpu - opCpu) * 100.00),
|
||||
(aCpu - oaCpu), 1);
|
||||
if (usage < 0) {
|
||||
usage = 0;
|
||||
}
|
||||
else if (usage > 100)
|
||||
{
|
||||
usage = 100;
|
||||
}
|
||||
|
||||
}
|
||||
opCpu = pCpu;
|
||||
oaCpu = aCpu;
|
||||
}
|
||||
pJif = pCpu;
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CPU使用率
|
||||
*
|
||||
* @return CPU使用率,单位:%
|
||||
*/
|
||||
public static double getSysCpuUsagePercent() {
|
||||
double usage = 0.0;
|
||||
if (initCpu) {
|
||||
initCpu = false;
|
||||
RandomAccessFile reader = null;
|
||||
try {
|
||||
reader = new RandomAccessFile("/proc/stat",
|
||||
"r");
|
||||
String load;
|
||||
load = reader.readLine();
|
||||
String[] toks = load.split(" ");
|
||||
oIdle = Double.parseDouble(toks[5]);
|
||||
oCpu = Double.parseDouble(toks[2])
|
||||
+ Double.parseDouble(toks[3])
|
||||
+ Double.parseDouble(toks[4])
|
||||
+ Double.parseDouble(toks[6])
|
||||
+ Double.parseDouble(toks[8])
|
||||
+ Double.parseDouble(toks[7]);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
FileUtil.closeRandomAccessFile(reader);
|
||||
}
|
||||
} else {
|
||||
RandomAccessFile reader = null;
|
||||
try {
|
||||
reader = new RandomAccessFile("/proc/stat", "r");
|
||||
String load;
|
||||
load = reader.readLine();
|
||||
String[] toks = load.split(" ");
|
||||
double cIdle = Double.parseDouble(toks[5]);
|
||||
double cCpu = Double.parseDouble(toks[2])
|
||||
+ Double.parseDouble(toks[3])
|
||||
+ Double.parseDouble(toks[4])
|
||||
+ Double.parseDouble(toks[6])
|
||||
+ Double.parseDouble(toks[8])
|
||||
+ Double.parseDouble(toks[7]);
|
||||
if (0 != ((cCpu + cIdle) - (oCpu + oIdle))) {
|
||||
usage = DoubleUtils.div((100.00 * ((cCpu - oCpu))),
|
||||
((cCpu + cIdle) - (oCpu + oIdle)), 1);
|
||||
if (usage < 0) {
|
||||
usage = 0;
|
||||
}
|
||||
else if (usage > 100)
|
||||
{
|
||||
usage = 100;
|
||||
}
|
||||
}
|
||||
oCpu = cCpu;
|
||||
oIdle = cIdle;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
FileUtil.closeRandomAccessFile(reader);
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
private static String[] getCpuWithPid(int pid) {
|
||||
String cpuPath = "/proc/" + pid + "/stat";
|
||||
String cpu = "";
|
||||
String[] result = new String[3];
|
||||
|
||||
File f = new File(cpuPath);
|
||||
// 部分进程信息无法读取
|
||||
if (!f.exists() || !f.canRead())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
FileReader fr = null;
|
||||
BufferedReader localBufferedReader = null;
|
||||
|
||||
try {
|
||||
fr = new FileReader(f);
|
||||
localBufferedReader = new BufferedReader(fr, 8192);
|
||||
cpu = localBufferedReader.readLine();
|
||||
if (null != cpu) {
|
||||
String[] cpuSplit = cpu.split(" ");
|
||||
result[0] = cpuSplit[1];
|
||||
result[1] = cpuSplit[13];
|
||||
result[2] = cpuSplit[14];
|
||||
}
|
||||
}catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
FileUtil.closeReader(localBufferedReader);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String[] getSystemCpu() {
|
||||
String cpuPath = "/proc/stat";
|
||||
String cpu = "";
|
||||
String[] result = new String[7];
|
||||
|
||||
File f = new File(cpuPath);
|
||||
if (!f.exists() || !f.canRead())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
FileReader fr = null;
|
||||
BufferedReader localBufferedReader = null;
|
||||
|
||||
try {
|
||||
fr = new FileReader(f);
|
||||
localBufferedReader = new BufferedReader(fr, 8192);
|
||||
cpu = localBufferedReader.readLine();
|
||||
if (null != cpu) {
|
||||
result = cpu.split(" ");
|
||||
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
FileUtil.closeReader(localBufferedReader);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getCpuUsageByCmd() {
|
||||
java.lang.Process process;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
String cmd = "dumpsys cpuinfo";
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(
|
||||
new String[] { "sh", "-c", cmd });
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
process.getInputStream()));
|
||||
while (((line = br.readLine()) != null)) {
|
||||
// 去掉空白行数据
|
||||
line = line.trim();
|
||||
if (line.equals("")) {
|
||||
continue;
|
||||
}
|
||||
sb.append(line);
|
||||
sb.append("\r\n");
|
||||
}
|
||||
try {
|
||||
process.waitFor();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取进程的CPU使用时间片
|
||||
*
|
||||
* @return 进程的CPU使用时间片
|
||||
*/
|
||||
public long getJif() {
|
||||
return (long)pJif;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.utils
|
||||
|
||||
import java.lang.Exception
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
||||
object DoubleUtils {
|
||||
|
||||
fun mul(d1: Double, d2: Double): Double {
|
||||
val bd1 = BigDecimal(d1.toString())
|
||||
val bd2 = BigDecimal(d2.toString())
|
||||
return try {
|
||||
bd1.multiply(bd2).toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun div(d1: Double, d2: Double, scale: Int): Double {
|
||||
val bd1 = BigDecimal(d1.toString())
|
||||
val bd2 = BigDecimal(d2.toString())
|
||||
return try {
|
||||
// 直接向下取整
|
||||
bd1.divide(bd2, scale, BigDecimal.ROUND_DOWN).toDouble()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 四舍五入
|
||||
* scale:保留多少个小数位
|
||||
*/
|
||||
@JvmStatic
|
||||
fun keepDecimal(v: Double, scale: Int = 1): Double {
|
||||
if (scale < 0) {
|
||||
throw IllegalArgumentException("The scale must be a positive integer or zero")
|
||||
}
|
||||
return BigDecimal(v).setScale(scale, BigDecimal.ROUND_HALF_UP).toDouble()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.utils
|
||||
|
||||
import android.net.LocalSocket
|
||||
import android.util.Log
|
||||
import java.io.*
|
||||
import java.lang.Exception
|
||||
import java.net.Socket
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
object FileUtil {
|
||||
private const val TAG = "FileUtil"
|
||||
const val separator = "/"
|
||||
fun isPathStringValid(path: String?): Boolean {
|
||||
if (null == path || path.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
if (path.contains(":") || path.contains("*") || path.contains("?")
|
||||
|| path.contains("\"") || path.contains("<")
|
||||
|| path.contains(">") || path.contains("|")
|
||||
) {
|
||||
Log.w(TAG, "filename can not contains:*:?\"<>|")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun isPath(path: String): Boolean {
|
||||
return path.contains(separator) || path.contains("\\")
|
||||
}
|
||||
|
||||
fun getPath(path: String): String {
|
||||
val la = path.lastIndexOf(separator)
|
||||
return path.substring(0, la)
|
||||
}
|
||||
|
||||
fun convertValidFilePath(path: String, defPosfix: String): String {
|
||||
var filePath = path
|
||||
if (path.contains(separator) || path.contains("\\")) {
|
||||
val la = filePath.lastIndexOf(".")
|
||||
if (la < 0) {
|
||||
filePath = path + defPosfix
|
||||
} else {
|
||||
val temp = filePath.substring(la)
|
||||
if (temp.contains(separator) || temp.contains("\\")) {
|
||||
// "."是目录名的一部分而不是后缀名的情况
|
||||
filePath = path + defPosfix
|
||||
}
|
||||
// else fileName = fileName
|
||||
}
|
||||
} else {
|
||||
if (!path.contains(".")) // 没有有后缀
|
||||
{
|
||||
filePath = filePath + defPosfix
|
||||
}
|
||||
}
|
||||
return filePath
|
||||
}
|
||||
|
||||
fun isFileExists(file: String?): Boolean {
|
||||
try {
|
||||
val f = File(file)
|
||||
if (!f.exists()) {
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun isFileValid(f: File): Boolean {
|
||||
if (!f.exists()) {
|
||||
try {
|
||||
f.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
return false
|
||||
}
|
||||
f.delete()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun isFileValid(parent: File?, name: String?): Boolean {
|
||||
val f = File(parent, name)
|
||||
return isFileValid(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存在的文件
|
||||
*
|
||||
* @param filePath
|
||||
*/
|
||||
fun delExistFile(filePath: String?) {
|
||||
val f = File(filePath)
|
||||
if (f.exists()) f.delete()
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭bufferReader
|
||||
*
|
||||
* @param br
|
||||
*/
|
||||
@JvmStatic
|
||||
fun closeReader(br: Reader?) {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭Writer
|
||||
*
|
||||
* @param wr
|
||||
*/
|
||||
fun closeWriter(wr: Writer?) {
|
||||
if (wr != null) {
|
||||
try {
|
||||
wr.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* flush Writer
|
||||
*
|
||||
* @param wr
|
||||
*/
|
||||
fun flushWriter(wr: Writer?) {
|
||||
if (wr != null) {
|
||||
try {
|
||||
wr.flush()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输入流的关闭
|
||||
*
|
||||
* @param in
|
||||
*/
|
||||
fun closeInputStream(`in`: InputStream?) {
|
||||
if (`in` != null) {
|
||||
try {
|
||||
`in`.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出流的关闭
|
||||
*
|
||||
* @param out
|
||||
*/
|
||||
fun closeOutputStream(out: OutputStream?) {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件管道的关闭
|
||||
*
|
||||
* @param chl
|
||||
*/
|
||||
fun closeFileChannel(chl: FileChannel?) {
|
||||
if (chl != null) {
|
||||
try {
|
||||
chl.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RandomAccessFile的关闭
|
||||
*
|
||||
* @param f RandomAccessFile对象
|
||||
*/
|
||||
@JvmStatic
|
||||
fun closeRandomAccessFile(f: RandomAccessFile?) {
|
||||
if (f != null) {
|
||||
try {
|
||||
f.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket的关闭
|
||||
*
|
||||
* @param s Socket对象
|
||||
*/
|
||||
fun colseSocket(s: Socket?) {
|
||||
if (s != null) {
|
||||
try {
|
||||
s.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalSocket的关闭
|
||||
*
|
||||
* @param s Socket对象
|
||||
*/
|
||||
fun colseLocalSocket(s: LocalSocket?) {
|
||||
if (s != null) {
|
||||
try {
|
||||
s.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteFile(file: File) {
|
||||
if (file.exists()) { // 判断文件是否存在
|
||||
if (file.isFile) { // 判断是否是文件
|
||||
file.delete() // delete()方法 你应该知道 是删除的意思;
|
||||
} else if (file.isDirectory) { // 否则如果它是一个目录
|
||||
val files = file.listFiles() // 声明目录下所有的文件 files[];
|
||||
for (i in files.indices) { // 遍历目录下所有的文件
|
||||
deleteFile(files[i]) // 把每个文件 用这个方法进行迭代
|
||||
}
|
||||
}
|
||||
file.delete()
|
||||
} else {
|
||||
Log.e(TAG, "文件不存在!" + "n")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝文件
|
||||
*
|
||||
* @param s 源文件
|
||||
* @param t 目标文件
|
||||
*/
|
||||
fun copyFile(s: File?, t: File) {
|
||||
var fi: FileInputStream? = null
|
||||
var fo: FileOutputStream? = null
|
||||
var `in`: FileChannel? = null
|
||||
var out: FileChannel? = null
|
||||
try {
|
||||
if (!t.exists()) {
|
||||
t.createNewFile()
|
||||
}
|
||||
fi = FileInputStream(s)
|
||||
fo = FileOutputStream(t)
|
||||
`in` = fi.channel
|
||||
out = fo.channel
|
||||
// 连接两个通道,并且从in通道读取,然后写入out通道
|
||||
`in`.transferTo(0, `in`.size(), out)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
closeOutputStream(fo)
|
||||
closeInputStream(fi)
|
||||
closeFileChannel(`in`)
|
||||
closeFileChannel(out)
|
||||
}
|
||||
}
|
||||
|
||||
fun copyInputToFile(`in`: InputStream?, path: String?) {
|
||||
var bis: BufferedInputStream? = null
|
||||
var fos: FileOutputStream? = null
|
||||
try {
|
||||
val buffer = ByteArray(10 * 1024)
|
||||
bis = BufferedInputStream(`in`)
|
||||
fos = FileOutputStream(path)
|
||||
var a = bis.read(buffer, 0, buffer.size)
|
||||
while (a != -1) {
|
||||
fos.write(buffer, 0, a)
|
||||
fos.flush()
|
||||
a = bis.read(buffer, 0, buffer.size)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
closeOutputStream(fos)
|
||||
closeInputStream(bis)
|
||||
closeInputStream(`in`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.utils;
|
||||
|
||||
import android.os.Debug;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class HookUtils {
|
||||
private static final String TAG = "HookUtils";
|
||||
|
||||
/**
|
||||
* 单位为MB
|
||||
* 0: 总PSS,1: dalvik总Pss, 2: native总Pss, 3: other总Pss
|
||||
* @param memoryInfo
|
||||
* @return
|
||||
*/
|
||||
public static double[] getPssArray(Debug.MemoryInfo memoryInfo) {
|
||||
double[] pssArray = new double[4];
|
||||
if (memoryInfo != null) {
|
||||
double totalPss = DoubleUtils.keepDecimal(memoryInfo.getTotalPss() / 1024.0, 3);
|
||||
// Log.d(TAG, "===========Hook PSS 开始:===========\n");
|
||||
// Log.d(TAG, "总PSS内存为:" + totalPss);
|
||||
pssArray[0] = totalPss;
|
||||
try {
|
||||
Field dalvikField = memoryInfo.getClass().getField("dalvikSwappedOutPss");
|
||||
double dalvikPss = DoubleUtils.keepDecimal(memoryInfo.dalvikPss / 1024.0, 3);
|
||||
double dalvikSwappedOutPss = DoubleUtils.keepDecimal((int) dalvikField.get(memoryInfo) / 1024.0, 3);
|
||||
pssArray[1] = dalvikPss + dalvikSwappedOutPss;
|
||||
// Log.d(TAG, "dalvikPss is:" + dalvikSwappedOutPss);
|
||||
// Log.d(TAG, "dalvikSwappedOutPss is:" + dalvikPss + "\n\n");
|
||||
|
||||
Field nativeField = memoryInfo.getClass().getField("nativeSwappedOutPss");
|
||||
double nativePss = DoubleUtils.keepDecimal(memoryInfo.nativePss / 1024.0, 3);
|
||||
double nativeSwappedOutPss = DoubleUtils.keepDecimal((int) nativeField.get(memoryInfo) / 1024.0, 3);
|
||||
pssArray[2] = nativePss + nativeSwappedOutPss;
|
||||
// Log.d(TAG, "nativePss is:" + nativePss);
|
||||
// Log.d(TAG, "nativeSwappedOutPss is:" + nativeSwappedOutPss + "\n\n");
|
||||
|
||||
Field otherField = memoryInfo.getClass().getField("otherSwappedOutPss");
|
||||
double otherPss = DoubleUtils.keepDecimal(memoryInfo.otherPss / 1024.0, 3);
|
||||
double otherSwappedOutPss = DoubleUtils.keepDecimal((int) otherField.get(memoryInfo) / 1024.0, 3);
|
||||
pssArray[3] = otherPss + otherSwappedOutPss;
|
||||
// Log.d(TAG, "otherPss is:" + otherPss);
|
||||
// Log.d(TAG, "otherSwappedOutPss is:" + otherSwappedOutPss + "\n\n");
|
||||
//
|
||||
// Log.d(TAG, "===========Hook PSS 结束!===========\n");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
return pssArray;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.zhjt.mogo_core_function_devatools.monitor.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.os.Debug;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 内存信息工具类。
|
||||
*/
|
||||
public class MemUtils {
|
||||
|
||||
public static double[] getPssArray(Context context, int[] pids) {
|
||||
Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids);
|
||||
if (memoryArray.length > 0) {
|
||||
return HookUtils.getPssArray(memoryArray[0]);
|
||||
} else {
|
||||
return new double[4];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单位MB
|
||||
*/
|
||||
public static double getPssMemValue(Context context, int[] pids) {
|
||||
double value = 0.0;
|
||||
Debug.MemoryInfo[] memoryArray = getMemoryInfo(context, pids);
|
||||
if (memoryArray.length > 0) {
|
||||
value = DoubleUtils.keepDecimal(memoryArray[0].getTotalPss() / 1024.0, 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能获取本应用的PSS内存占系统百分比:单位%,保留1位小数
|
||||
* @return
|
||||
*/
|
||||
public static double getPidMemPercent() {
|
||||
double percent = 0.0;
|
||||
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(memInfo);
|
||||
int pidTotalPss = memInfo.getTotalPss();
|
||||
long systemTotalMem = getTotalMem();
|
||||
percent = DoubleUtils.keepDecimal(pidTotalPss * 100.0 / systemTotalMem, 1);
|
||||
return percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本应用进程总pss内存,单位MB
|
||||
*/
|
||||
public static double getPssMem() {
|
||||
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
|
||||
Debug.getMemoryInfo(memInfo);
|
||||
return DoubleUtils.keepDecimal(memInfo.getTotalPss() / 1024.0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统空闲内存占用百分比: 单位%
|
||||
* @return
|
||||
*/
|
||||
public static double getSystemMemPercent() {
|
||||
double percent = 0.0;
|
||||
long[] memInfo = getMemInfo();
|
||||
long free = memInfo[1] + memInfo[2] + memInfo[3];
|
||||
long total = memInfo[0];
|
||||
percent = DoubleUtils.keepDecimal(free * 100.0 / total, 1);
|
||||
return percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取空闲内存
|
||||
*
|
||||
* @return 空闲内存,单位KB
|
||||
*/
|
||||
public static long getFreeMem() {
|
||||
long[] memInfo = getMemInfo();
|
||||
return memInfo[1] + memInfo[2] + memInfo[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取总内存
|
||||
*
|
||||
* @return 总内存,单位KB
|
||||
*/
|
||||
public static long getTotalMem() {
|
||||
long[] memInfo = getMemInfo();
|
||||
return memInfo[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内存信息:total、free、buffers、cached,单位KB
|
||||
*
|
||||
* @return 内存信息
|
||||
*/
|
||||
public static long[] getMemInfo() {
|
||||
long memInfo[] = new long[4];
|
||||
try {
|
||||
Class<?> procClazz = Class.forName("android.os.Process");
|
||||
Class<?> paramTypes[] = new Class[] { String.class, String[].class,
|
||||
long[].class };
|
||||
Method readProclines = procClazz.getMethod("readProcLines",
|
||||
paramTypes);
|
||||
Object args[] = new Object[3];
|
||||
final String[] memInfoFields = new String[] { "MemTotal:",
|
||||
"MemFree:", "Buffers:", "Cached:" };
|
||||
long[] memInfoSizes = new long[memInfoFields.length];
|
||||
memInfoSizes[0] = 30;
|
||||
memInfoSizes[1] = -30;
|
||||
args[0] = "/proc/meminfo";
|
||||
args[1] = memInfoFields;
|
||||
args[2] = memInfoSizes;
|
||||
if (null != readProclines) {
|
||||
readProclines.invoke(null, args);
|
||||
for (int i = 0; i < memInfoSizes.length; i++) {
|
||||
memInfo[i] = memInfoSizes[i];
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return memInfo;
|
||||
}
|
||||
|
||||
public static long[] getHeapNative() {
|
||||
int Native_HeapSize = 0;
|
||||
int Native_HeapAlloc = 1;
|
||||
long[] value = new long[2];
|
||||
value[Native_HeapSize] = Debug.getNativeHeapSize() >> 10;
|
||||
value[Native_HeapAlloc] = Debug.getNativeHeapAllocatedSize() >> 10;
|
||||
return value;
|
||||
}
|
||||
|
||||
public static long[] getHeapDalvik() {
|
||||
int Total_HeapSize = 0;
|
||||
int Total_HeapAlloc = 1;
|
||||
|
||||
long[] value_total = new long[2];
|
||||
value_total[Total_HeapSize] = Runtime.getRuntime().totalMemory() >> 10;
|
||||
value_total[Total_HeapAlloc] = (Runtime.getRuntime().totalMemory() - Runtime
|
||||
.getRuntime().freeMemory()) >> 10;
|
||||
|
||||
long[] value_native = getHeapNative();
|
||||
|
||||
int Dalvik_HeapSize = 0;
|
||||
int Dalvik_HeapAlloc = 1;
|
||||
long[] value_dalvik = new long[2];
|
||||
value_dalvik[Dalvik_HeapSize] = value_total[Total_HeapSize]
|
||||
- value_native[0];
|
||||
value_dalvik[Dalvik_HeapAlloc] = value_total[Total_HeapAlloc]
|
||||
- value_native[1];
|
||||
|
||||
return value_dalvik;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取堆内存数据,精确到KB Get VM Heap Size by calling:
|
||||
* Runtime.getRuntime().totalMemory(); Get Allocated VM Memory by calling:
|
||||
* Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
* Get VM Heap Size Limit by calling: Runtime.getRuntime().maxMemory() Get
|
||||
* Native Allocated Memory by calling: Debug.getNativeHeapAllocatedSize();
|
||||
*/
|
||||
public static long[] getVM() {
|
||||
long[] value = new long[5];
|
||||
value[0] = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
|
||||
.freeMemory()) >> 10;
|
||||
value[1] = Runtime.getRuntime().totalMemory() >> 10;
|
||||
|
||||
value[2] = Debug.getNativeHeapAllocatedSize() >> 10;
|
||||
value[3] = Debug.getNativeHeapSize() >> 10;
|
||||
value[4] = Debug.getGlobalAllocSize() >> 10;
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Debug.MemoryInfo[] getMemoryInfo(Context context, int[] pids) {
|
||||
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
return am.getProcessMemoryInfo(pids);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.eagle.core.function.hmi">
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
@@ -70,6 +70,7 @@ import com.mogo.eagle.core.function.hmi.ui.setting.IPCReportWindow
|
||||
import com.mogo.eagle.core.function.hmi.ui.setting.ReportListFloatWindow
|
||||
import com.mogo.eagle.core.function.hmi.ui.tools.AdUpgradeDialog
|
||||
import com.mogo.eagle.core.function.hmi.ui.tools.AutoPilotAndCheckView
|
||||
import com.mogo.eagle.core.function.hmi.ui.tools.MaskView
|
||||
import com.mogo.eagle.core.function.hmi.ui.widget.V2XNotificationView
|
||||
import com.mogo.eagle.core.utilcode.kotlin.*
|
||||
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
|
||||
@@ -127,6 +128,9 @@ import java.util.*
|
||||
// 检测、自动驾驶速度设置
|
||||
private var toolsViewFloat: WarningFloat.Builder? = null
|
||||
|
||||
private var maskViewFloat: WarningFloat.Builder? = null
|
||||
private var maskView: MaskView? = null
|
||||
|
||||
private var upgradeTipsView: (() -> View)? = null
|
||||
|
||||
// HMI 视图控件代理
|
||||
@@ -178,7 +182,7 @@ import java.util.*
|
||||
}
|
||||
}
|
||||
|
||||
/*ivCameraIcon?.setOnLongClickListener {
|
||||
/**ivCameraIcon?.setOnLongClickListener {
|
||||
activity?.let { it1 -> CarcorderPreviewView.show(it1) }
|
||||
true
|
||||
}*/
|
||||
@@ -956,6 +960,66 @@ import java.util.*
|
||||
cameraListView?.showNoSignal()
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示蒙层
|
||||
*/
|
||||
override fun showMaskView() {
|
||||
if (maskViewFloat == null) {
|
||||
context?.let {
|
||||
if (maskViewFloat == null) {
|
||||
if (maskView == null) {
|
||||
maskView = MaskView(it)
|
||||
}
|
||||
|
||||
maskViewFloat = WarningFloat.with(it)
|
||||
.setTag("MaskViewFloat")
|
||||
.setLayout(maskView!!)
|
||||
.setSidePattern(SidePattern.LEFT)
|
||||
.setGravity(Gravity.CENTER)
|
||||
.setImmersionStatusBar(true)
|
||||
.setWindowHeight(LayoutParams.MATCH_PARENT)
|
||||
.setWindowWidth(LayoutParams.MATCH_PARENT)
|
||||
// .setCountDownTime(6000)
|
||||
.setAnimator(object : DefaultAnimator() {
|
||||
override fun enterAnim(
|
||||
view: View,
|
||||
params: LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? =
|
||||
super.enterAnim(view, params, windowManager, sidePattern)
|
||||
?.apply {
|
||||
interpolator = OvershootInterpolator()
|
||||
}
|
||||
|
||||
override fun exitAnim(
|
||||
view: View,
|
||||
params: LayoutParams,
|
||||
windowManager: WindowManager,
|
||||
sidePattern: SidePattern
|
||||
): Animator? =
|
||||
super.exitAnim(view, params, windowManager, sidePattern)
|
||||
?.setDuration(200)
|
||||
})
|
||||
.addWarningStatusListener(object : IMoGoWarningStatusListener {
|
||||
override fun onDismiss() {
|
||||
maskViewFloat = null
|
||||
maskView = null
|
||||
// TODO:("")
|
||||
}
|
||||
})
|
||||
.show()
|
||||
} else {
|
||||
maskViewFloat?.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hideMaskView() {
|
||||
dismissMaskView()
|
||||
}
|
||||
|
||||
override fun showToolsView() {
|
||||
if (toolsViewFloat == null) {
|
||||
showToolsFloat()
|
||||
@@ -1052,6 +1116,14 @@ import java.util.*
|
||||
}
|
||||
}
|
||||
|
||||
private fun dismissMaskView() {
|
||||
maskViewFloat?.let {
|
||||
WarningFloat.dismiss(it.config.floatTag, false)
|
||||
maskViewFloat = null
|
||||
maskView = null
|
||||
}
|
||||
}
|
||||
|
||||
private var isLeftLight :Boolean = false
|
||||
private var isRightLight :Boolean = false
|
||||
private var isDisappare :Boolean = false
|
||||
|
||||
@@ -65,6 +65,7 @@ import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr
|
||||
import com.mogo.eagle.core.utilcode.mogo.toast.TipToast
|
||||
import com.mogo.eagle.core.utilcode.util.*
|
||||
import com.mogo.map.MogoMap
|
||||
import com.mogo.map.MogoMapUIController
|
||||
import com.mogo.map.uicontroller.VisualAngleMode
|
||||
import com.mogo.map.uicontroller.VisualAngleMode.*
|
||||
import com.mogo.module.service.routeoverlay.*
|
||||
@@ -151,6 +152,8 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
|
||||
}
|
||||
|
||||
private var isStarted = false
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_debug_setting, this, true)
|
||||
initView()
|
||||
@@ -247,6 +250,23 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔戒控制
|
||||
*/
|
||||
tbMojie.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
if (isChecked) {
|
||||
buttonView.setCompoundDrawables(null, null, iconDown, null)
|
||||
btnOpenAllGestures.visibility = View.VISIBLE
|
||||
} else {
|
||||
buttonView.setCompoundDrawables(null, null, iconRight, null)
|
||||
btnOpenAllGestures.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
btnOpenAllGestures.setOnClickListener {
|
||||
MogoMapUIController.getInstance().setAllGesturesEnabled(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 版本信息
|
||||
*/
|
||||
@@ -835,6 +855,17 @@ class DebugSettingView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
btnBrakeThreshold.setOnLongClickListener {
|
||||
if (!isStarted) {
|
||||
CallerDevaToolsManager.startMonitor()
|
||||
isStarted = true
|
||||
} else {
|
||||
CallerDevaToolsManager.stopMonitor()
|
||||
isStarted = false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
//设置连接司机屏IP
|
||||
btnConnectServerIp.setOnClickListener {
|
||||
val ip = etConnectServerIp.text.toString()
|
||||
|
||||
@@ -31,9 +31,9 @@ class BusOperationView @JvmOverloads constructor(
|
||||
|
||||
override fun changerOperationStatus(isOut: Boolean) {
|
||||
if (isOut){
|
||||
ochBusOperationStatusCTV.setText("收车")
|
||||
ochBusOperationStatusCTV.text = "收车"
|
||||
}else{
|
||||
ochBusOperationStatusCTV.setText("出车")
|
||||
ochBusOperationStatusCTV.text = "出车"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.mogo.eagle.core.function.hmi.ui.tools
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import com.mogo.eagle.core.function.hmi.R
|
||||
|
||||
/**
|
||||
* @author lixiaopeng
|
||||
* 蒙层view
|
||||
*/
|
||||
class MaskView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
|
||||
private val TAG = "MaskView"
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_mask, this, true)
|
||||
initView()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var speedLimit: Double = 0.0
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun initView() {
|
||||
// background = ColorDrawable(Color.parseColor("#F0151D41"))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,12 +4,19 @@ import static com.mogo.eagle.core.data.deva.chain.ChainConstant.CHAIN_ALIAS_CODE
|
||||
import static com.mogo.eagle.core.data.deva.chain.ChainConstant.CHAIN_LINK_INIT;
|
||||
import static com.mogo.eagle.core.data.deva.chain.ChainConstant.CHAIN_LINK_LOG_CONNECT_STATUS;
|
||||
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_HMI;
|
||||
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_MAP;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -17,13 +24,22 @@ import androidx.annotation.Nullable;
|
||||
import com.alibaba.android.arouter.launcher.ARouter;
|
||||
import com.mogo.commons.debug.DebugConfig;
|
||||
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
|
||||
import com.mogo.eagle.core.data.config.HdMapBuildConfig;
|
||||
import com.mogo.eagle.core.function.api.base.IMoGoFunctionProvider;
|
||||
import com.mogo.eagle.core.function.api.setting.IMoGoSkinModeChangeListener;
|
||||
import com.mogo.eagle.core.function.call.analytics.AnalyticsManager;
|
||||
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotManager;
|
||||
import com.mogo.eagle.core.function.call.setting.CallerSkinModeListenerManager;
|
||||
import com.mogo.eagle.core.function.hmi.R;
|
||||
import com.mogo.eagle.core.function.main.moujie.BluetoothMonitorReceiver;
|
||||
import com.mogo.eagle.core.function.main.moujie.ConnectBluetoothEvent;
|
||||
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
|
||||
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
|
||||
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
|
||||
import com.mogo.eagle.core.utilcode.util.BarUtils;
|
||||
import com.mogo.eagle.core.utilcode.util.ToastUtils;
|
||||
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
|
||||
import com.mogo.map.MogoMapUIController;
|
||||
import com.mogo.service.intent.IMogoIntentListener;
|
||||
import com.rousetime.android_startup.model.CostTimesModel;
|
||||
import com.zhjt.service.chain.ChainLog;
|
||||
@@ -31,8 +47,15 @@ import com.zhjt.service.chain.TracingConstants;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* 针对作为Launcher的情况,做个性化操作
|
||||
@@ -45,12 +68,37 @@ public class MainLauncherActivity extends MainActivity implements IMogoIntentLis
|
||||
private final static Handler handlerV2XEvent = new Handler();
|
||||
private static Runnable runnableV2XEvent;
|
||||
|
||||
private volatile double accelerated;//加速度
|
||||
private Timer timerHorn;
|
||||
private Timer timerAcc;
|
||||
|
||||
private BluetoothMonitorReceiver mBluetoothReceiver = null;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private List<BluetoothDevice> mAreadlyConnectedList = new ArrayList<>();//已连接设备集合
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
DebugConfig.setNeedRequestUserInfo(true);
|
||||
EventBus.getDefault().register(this);
|
||||
CallerLogger.INSTANCE.i(M_HMI + TAG, "onCreate");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
initBluetooth();
|
||||
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (!mBluetoothAdapter.isEnabled()) {
|
||||
mBluetoothAdapter.enable();
|
||||
}
|
||||
mBluetoothAdapter.startDiscovery();
|
||||
|
||||
showBondedDevice(mBluetoothAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,6 +203,112 @@ public class MainLauncherActivity extends MainActivity implements IMogoIntentLis
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { //向上长按 开启自动驾驶
|
||||
long currentTime = System.currentTimeMillis() / (1000);
|
||||
long oldTime = SharedPrefsMgr.getInstance(getContext()).getLong("old_time_up", 0);
|
||||
// Log.d("liyz", "time = " + (currentTime - oldTime));
|
||||
if (currentTime - oldTime > 6) {
|
||||
SharedPrefsMgr.getInstance(getContext()).putLong("old_time_up", System.currentTimeMillis() / 1000);
|
||||
// ToastUtils.showShort("长按 ↑↑↑ 开启自动驾驶");
|
||||
CallerAutoPilotManager.INSTANCE.setControlAutopilotCarAuto(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { //下和中间键盘长按,减速为-2
|
||||
long currentTime = System.currentTimeMillis() / (1000);
|
||||
long oldTime = SharedPrefsMgr.getInstance(getContext()).getLong("old_time_down", 0);
|
||||
if (currentTime - oldTime > 6) {
|
||||
SharedPrefsMgr.getInstance(getContext()).putLong("old_time_down", System.currentTimeMillis() / 1000);
|
||||
// ToastUtils.showShort("长按 ↓↓↓ 减速 -2 ");
|
||||
sendAcc(true, -2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
// Log.d("liyz", "dispatchTouchEvent event.getX() = " + event.getX() + "--event.getY() = " + event.getY() + "--action = " + event.getAction());
|
||||
if (event.getX() == -1469.6875 && event.getY() == 530.0) { //向左变道
|
||||
// ToastUtils.showShort("← 向左变道");
|
||||
// MogoMapUIController.getInstance().setLockMode( true );
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorChangeLaneLeft();
|
||||
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 132.5) { //鸣笛
|
||||
// ToastUtils.showShort("↑ 鸣笛 ");
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorSetHorn(1);
|
||||
|
||||
if (timerHorn == null) {
|
||||
timerHorn = new Timer();
|
||||
}
|
||||
timerHorn.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorSetHorn(2);
|
||||
timerHorn = null;
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return true;
|
||||
} else if (event.getX() == -863.4375 && event.getY() == 690.0) { //向右变道
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorChangeLaneRight();
|
||||
// ToastUtils.showShort("→ 向右变道");
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 1099.0) { // -1减速
|
||||
sendAcc(true, -1);
|
||||
// ToastUtils.showShort("↓ 减速 -1 ");
|
||||
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 690.0) { //复原
|
||||
// ToastUtils.showShort("O 复原 ");
|
||||
sendAcc(false, 0.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.dispatchTouchEvent(event);
|
||||
|
||||
// if (SharedPrefsMgr.getInstance(getContext()).getBoolean("BLUETOOTH", false)) {
|
||||
// return false;
|
||||
// } else {
|
||||
// return super.dispatchTouchEvent(event);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
//← x:-1469.6875 y:530.0
|
||||
//↑ x:-1096.25 y:132.5
|
||||
//→ x:-863.4375 y: 690.0
|
||||
//↓ x:-1096.25 y:1099.0
|
||||
//O x:-1096.25 y:690.0
|
||||
// Log.d("liyz", "onTouchEvent event.getX() = " + event.getX() + "--event.getY() = " + event.getY());
|
||||
|
||||
if (event.getX() == -1469.6875 && event.getY() == 530.0) {
|
||||
// ToastUtils.showShort("←");
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 132.5) {
|
||||
// ToastUtils.showShort("↑");
|
||||
return true;
|
||||
} else if (event.getX() == -863.4375 && event.getY() == 690.0) {
|
||||
// ToastUtils.showShort("→");
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 1099.0) {
|
||||
// ToastUtils.showShort("↓");
|
||||
return true;
|
||||
} else if (event.getX() == -1096.25 && event.getY() == 690.0) {
|
||||
// ToastUtils.showShort("O");
|
||||
return true;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSkinModeChange(int skinMode) {
|
||||
if (skinMode == 0) {
|
||||
@@ -163,4 +317,117 @@ public class MainLauncherActivity extends MainActivity implements IMogoIntentLis
|
||||
BarUtils.setStatusBarLightMode(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void sendAcc(boolean isSend, double acc) {
|
||||
if (isSend) {
|
||||
accelerated = acc;
|
||||
if (timerAcc == null) {
|
||||
timerAcc = new Timer();
|
||||
timerAcc.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorSetAcceleratedSpeed(accelerated);
|
||||
}
|
||||
}, 0, 500);
|
||||
}
|
||||
} else {
|
||||
if (timerAcc != null) {
|
||||
timerAcc.cancel();
|
||||
timerAcc = null;
|
||||
}
|
||||
CallerAutoPilotManager.INSTANCE.sendOperatorSetAcceleratedSpeed(acc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化蓝牙广播
|
||||
*/
|
||||
private void initBluetooth() {
|
||||
this.mBluetoothReceiver = new BluetoothMonitorReceiver();
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
// 监视蓝牙关闭和打开的状态
|
||||
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
|
||||
// 监视蓝牙设备与APP连接的状态
|
||||
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
|
||||
// 注册广播
|
||||
registerReceiver(this.mBluetoothReceiver, intentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找蓝牙连接过的蓝牙设备
|
||||
*
|
||||
* @param bluetoothAdapter
|
||||
*/
|
||||
private void showBondedDevice(BluetoothAdapter bluetoothAdapter) {
|
||||
mAreadlyConnectedList.clear();
|
||||
Set<BluetoothDevice> deviceList = bluetoothAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : deviceList) {
|
||||
try {
|
||||
//使用反射调用获取设备连接状态方法
|
||||
Method isConnectedMethod = BluetoothDevice.class.getDeclaredMethod("isConnected", (Class[]) null);
|
||||
isConnectedMethod.setAccessible(true);
|
||||
boolean isConnected = (boolean) isConnectedMethod.invoke(device, (Object[]) null);
|
||||
// Log.e("liyz", "showBondedDevice Name:" + device.getName() + " Mac:" + device.getAddress() + "---isConnected = " + isConnected);
|
||||
mAreadlyConnectedList.add(device);
|
||||
if (device.getName().equals("JX-05")) {
|
||||
SharedPrefsMgr.getInstance(getContext()).putString("BT_MAC", device.getAddress());
|
||||
}
|
||||
if (!isConnected) {
|
||||
UiThreadHandler.postDelayed(() -> {
|
||||
connectBluetooth(); //主动连接一次
|
||||
}, 4000L);
|
||||
}
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onAutoConnection(ConnectBluetoothEvent event) {
|
||||
connectBluetooth();
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动连接最近的一次连接
|
||||
*/
|
||||
private void connectBluetooth() {
|
||||
// Log.d("liyz", "connectBluetooth ----> size() = " + mAreadlyConnectedList.size());
|
||||
if (mAreadlyConnectedList.size() > 0) {
|
||||
try {
|
||||
Method method = BluetoothDevice.class.getMethod("createBond");
|
||||
String macAddress = SharedPrefsMgr.getInstance(getContext()).getString("BT_MAC");
|
||||
int pos = -1;
|
||||
for (int i = 0; i < mAreadlyConnectedList.size(); i++) {
|
||||
if (macAddress.equals(mAreadlyConnectedList.get(i).getAddress())) {
|
||||
pos = i;
|
||||
}
|
||||
}
|
||||
// Log.d("liyz", "connectBluetooth macAddress = " + macAddress + "---pos = " + pos);
|
||||
if (mAreadlyConnectedList.size() > pos && (pos >= 0)) {
|
||||
method.invoke(mAreadlyConnectedList.get(pos));
|
||||
}
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,8 +28,12 @@ import com.mogo.module.common.MogoModule;
|
||||
import com.mogo.module.common.MogoModulePaths;
|
||||
import com.mogo.module.service.ServiceConst;
|
||||
import com.zhidao.support.obu.ami.AmiClientManager;
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.CpuInfo;
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MemInfo;
|
||||
import com.zhjt.mogo_core_function_devatools.monitor.db.MonitorDb;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认初始化一些基础服务配置
|
||||
@@ -58,6 +62,7 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
|
||||
|
||||
//查询是否有版本的更新
|
||||
queryAppUpgrade();
|
||||
checkMonitorDb();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +80,18 @@ public abstract class MainMoGoApplication extends AbsMogoApplication {
|
||||
},5000);
|
||||
}
|
||||
|
||||
private void checkMonitorDb() {
|
||||
new Thread(() -> {
|
||||
long limitId = 50001;
|
||||
List<CpuInfo> cpuList = MonitorDb.getDb(this).monitorDao().getAllCPUById(limitId);
|
||||
List<MemInfo> memList = MonitorDb.getDb(this).monitorDao().getAllMemById(limitId);
|
||||
// 大于5w条清除
|
||||
if (cpuList.size() > 0 || memList.size() > 0) {
|
||||
this.deleteDatabase(MonitorDb.INTERNAL_DB_NAME);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化异常采集配置
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.mogo.eagle.core.function.main.moujie;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mogo.eagle.core.utilcode.mogo.storage.SharedPrefsMgr;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* @author lixiaopeng
|
||||
* @description
|
||||
* @since 2022/7/29
|
||||
*/
|
||||
public class BluetoothMonitorReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action != null) {
|
||||
switch (action) {
|
||||
case BluetoothAdapter.ACTION_STATE_CHANGED:
|
||||
int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
|
||||
switch (blueState) {
|
||||
case BluetoothAdapter.STATE_TURNING_ON:
|
||||
// Toast.makeText(context, "蓝牙正在打开", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_ON:
|
||||
// Toast.makeText(context, "蓝牙已经打开", Toast.LENGTH_SHORT).show();
|
||||
EventBus.getDefault().post(new ConnectBluetoothEvent());
|
||||
break;
|
||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
||||
// Toast.makeText(context, "蓝牙正在关闭", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case BluetoothAdapter.STATE_OFF:
|
||||
// Toast.makeText(context, "蓝牙已经关闭", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case BluetoothDevice.ACTION_ACL_CONNECTED:
|
||||
// Toast.makeText(context, "蓝牙设备已连接", Toast.LENGTH_SHORT).show();
|
||||
// Log.d("liyz", "蓝牙设备已连接 ----> ");
|
||||
// SharedPrefsMgr.getInstance(context).putBoolean("BLUETOOTH", true);
|
||||
break;
|
||||
|
||||
case BluetoothDevice.ACTION_ACL_DISCONNECTED:
|
||||
// SharedPrefsMgr.getInstance(context).putBoolean("BLUETOOTH", false);
|
||||
// Log.d("liyz", "蓝牙设备已断开 ----> ");
|
||||
// Toast.makeText(context, "蓝牙设备已断开", Toast.LENGTH_SHORT).show();
|
||||
EventBus.getDefault().post(new ConnectBluetoothEvent());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.mogo.eagle.core.function.main.moujie;
|
||||
|
||||
/**
|
||||
* @author lixiaopeng
|
||||
* @description 连接蓝牙事件
|
||||
* @since 2022/8/16
|
||||
*/
|
||||
public class ConnectBluetoothEvent {
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<gradient
|
||||
android:startColor="#6D7BAF"
|
||||
android:endColor="#3B4577"
|
||||
android:angle="315"
|
||||
/>
|
||||
<size
|
||||
android:width="150px"
|
||||
android:height="150px"
|
||||
/>
|
||||
</shape>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/icon_bus_operation_status" android:state_focused="true" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_bus_operation_status" android:state_focused="false" android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/icon_bus_operation_status" android:state_selected="true" />
|
||||
<item android:drawable="@drawable/icon_bus_operation_status" android:state_focused="true" />
|
||||
<item android:drawable="@drawable/icon_bus_operation_status_un" />
|
||||
</selector>
|
||||
@@ -243,6 +243,31 @@
|
||||
android:textSize="@dimen/dp_24"
|
||||
android:background="@drawable/radio_button_normal_background_right"/>
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/tbMojie"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/dp_10"
|
||||
android:background="@drawable/radio_button_normal_background_right"
|
||||
android:drawableEnd="@drawable/icon_right"
|
||||
android:padding="@dimen/dp_20"
|
||||
android:textOff="魔戒控制"
|
||||
android:textOn="魔戒控制" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOpenAllGestures"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/dp_5"
|
||||
android:paddingStart="@dimen/dp_20"
|
||||
android:paddingEnd="@dimen/dp_20"
|
||||
android:text="开启手势滑动"
|
||||
android:visibility="gone"
|
||||
android:textSize="@dimen/dp_24"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnIpcReboot"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:background="#00000000"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivWaringIcon"
|
||||
android:layout_width="132px"
|
||||
android:layout_height="132px"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -7,6 +7,7 @@
|
||||
android:layout_marginTop="40px"
|
||||
android:paddingBottom="40px"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:ignore="DuplicateIds">
|
||||
|
||||
<View
|
||||
@@ -22,6 +23,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="113px"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="4px"
|
||||
android:text="@string/bus_operation_title"
|
||||
android:textColor="@color/color_FFFFFF"
|
||||
android:textSize="42px" />
|
||||
@@ -33,12 +35,11 @@
|
||||
android:layout_below="@+id/tv_bus_operation_title"
|
||||
android:layout_marginStart="113px"
|
||||
android:layout_marginTop="40px"
|
||||
android:background="@drawable/bus_operation_status_bg"
|
||||
android:elevation="@dimen/dp_10"
|
||||
android:background="@drawable/bus_operation_status_bg_selector1"
|
||||
android:gravity="center"
|
||||
android:text="出车"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@drawable/bus_autopilot_text_color_selector"
|
||||
android:textColor="@color/bus_autopilot_text_color_normal"
|
||||
android:textSize="@dimen/module_mogo_och_autopilot_status_text_size"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
@@ -195,7 +195,7 @@ public class TrackObj {
|
||||
double dis = CoordinateUtils.calculateLineDistance(center[0], center[1], cacheData.getLongitude(), cacheData.getLatitude());
|
||||
// Log.d("emArrow-Track", "uuid : " + cacheData.getUuid() + " , type : " + cacheData.getType() + " , list size : " + objQueueList.size() + " , dis : " + dis);
|
||||
if (cacheData.getType() == TrafficTypeEnum.TYPE_TRAFFIC_ID_BUS.getType() || cacheData.getType() == TrafficTypeEnum.TYPE_TRAFFIC_ID_TRUCK.getType()) {
|
||||
return dis <= 0.8;
|
||||
return dis <= 0.6;
|
||||
} else {
|
||||
return dis <= 0.3;
|
||||
}
|
||||
|
||||
@@ -12,18 +12,20 @@ class RoadArrow {
|
||||
private const val TURN_LEFT_OR_U_TURN = 206 //左转或转向
|
||||
private const val TURN_RIGHT = 208 //右转
|
||||
private const val TURN_LEFT_OR_RIGHT = 210 //左右转向
|
||||
const val U_TURN = 211 //转向
|
||||
private const val U_TURN = 211 //转向
|
||||
private const val STRAIGHT_OR_LEFT_OR_RIGHT = 215 //直行或左转或右转
|
||||
private const val TURN_RIGHT_OR_U_TURN = 217 //右转或转向
|
||||
|
||||
fun isLeft(arrowId: Int): Boolean {
|
||||
return arrowId == STRAIGHT_OR_LEFT || arrowId == TURN_LEFT || arrowId == TURN_LEFT_OR_U_TURN || arrowId == TURN_LEFT_OR_RIGHT
|
||||
}
|
||||
|
||||
fun isStraight(arrowId: Int): Boolean {
|
||||
return arrowId == STRAIGHT_AHEAD || arrowId == STRAIGHT_OR_RIGHT || arrowId == STRAIGHT_OR_U_TURN
|
||||
return arrowId == STRAIGHT_AHEAD || arrowId == STRAIGHT_OR_RIGHT || arrowId == STRAIGHT_OR_U_TURN || arrowId == STRAIGHT_OR_LEFT_OR_RIGHT
|
||||
}
|
||||
|
||||
fun isRight(arrowId: Int): Boolean {
|
||||
return arrowId == TURN_RIGHT || arrowId == STRAIGHT_OR_RIGHT || arrowId == TURN_LEFT_OR_RIGHT
|
||||
return arrowId == STRAIGHT_OR_RIGHT || arrowId == TURN_RIGHT || arrowId == TURN_RIGHT_OR_U_TURN || arrowId == TURN_LEFT_OR_RIGHT
|
||||
}
|
||||
|
||||
fun isOnlyRight(arrowId: Int): Boolean {
|
||||
|
||||