├── .github
└── FUNDING.yml
├── .gitignore
├── .idea
├── codeStyles
│ └── Project.xml
├── encodings.xml
├── gradle.xml
├── jarRepositories.xml
├── misc.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ ├── app-release.apk
│ └── output.json
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── app-debug.apk
│ ├── java
│ └── com
│ │ └── projection
│ │ └── car
│ │ ├── ForgroundService.java
│ │ ├── MainActivity.java
│ │ ├── MediaCodecTool.java
│ │ ├── MsgProcess.java
│ │ └── Utils.java
│ ├── proto
│ ├── CarlifeAccelerationProto.proto
│ ├── CarlifeAuthenRequestProto.proto
│ ├── CarlifeAuthenResponseProto.proto
│ ├── CarlifeAuthenResultProto.proto
│ ├── CarlifeBTHfpConnectionProto.proto
│ ├── CarlifeBTHfpIndicationProto.proto
│ ├── CarlifeBTHfpRequestProto.proto
│ ├── CarlifeBTHfpResponseProto.proto
│ ├── CarlifeBTHfpStatusRequestProto.proto
│ ├── CarlifeBTHfpStatusResponseProto.proto
│ ├── CarlifeBTIdentifyResultIndProto.proto
│ ├── CarlifeBTPairInfoProto.proto
│ ├── CarlifeBTStartIdentifyReqProto.proto
│ ├── CarlifeBTStartPairReqProto.proto
│ ├── CarlifeCallRecordsListProto.proto
│ ├── CarlifeCallRecordsProto.proto
│ ├── CarlifeCarGpsProto.proto
│ ├── CarlifeCarHardKeyCodeProto.proto
│ ├── CarlifeCarSpeedProto.proto
│ ├── CarlifeConnectExceptionProto.proto
│ ├── CarlifeContactsListProto.proto
│ ├── CarlifeContactsProto.proto
│ ├── CarlifeDeviceInfoProto.proto
│ ├── CarlifeErrorCodeProto.proto
│ ├── CarlifeFeatureConfigListProto.proto
│ ├── CarlifeFeatureConfigProto.proto
│ ├── CarlifeGearInfoProto.proto
│ ├── CarlifeGyroscopeProto.proto
│ ├── CarlifeMediaInfoListProto.proto
│ ├── CarlifeMediaInfoProto.proto
│ ├── CarlifeMediaProgressBarProto.proto
│ ├── CarlifeModuleStatusListProto.proto
│ ├── CarlifeModuleStatusProto.proto
│ ├── CarlifeMusicInitProto.proto
│ ├── CarlifeNaviAssitantGuideInfoProto.proto
│ ├── CarlifeNaviNextTurnInfoProto.proto
│ ├── CarlifeOilProto.proto
│ ├── CarlifeProtocolVersionMatchStatusProto.proto
│ ├── CarlifeProtocolVersionProto.proto
│ ├── CarlifeStatisticsInfoProto.proto
│ ├── CarlifeSubscribeMobileCarLifeInfoListProto.proto
│ ├── CarlifeSubscribeMobileCarLifeInfoProto.proto
│ ├── CarlifeTTSInitProto.proto
│ ├── CarlifeTouchActionProto.proto
│ ├── CarlifeTouchEventAllDeviceProto.proto
│ ├── CarlifeTouchEventDeviceProto.proto
│ ├── CarlifeTouchEventProto.proto
│ ├── CarlifeTouchFlingProto.proto
│ ├── CarlifeTouchScrollProto.proto
│ ├── CarlifeTouchSinglePointProto.proto
│ ├── CarlifeVehicleInfoListProto.proto
│ ├── CarlifeVehicleInfoProto.proto
│ ├── CarlifeVideoEncoderInfoProto.proto
│ └── CarlifeVideoFrameRateProto.proto
│ └── res
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ ├── accessibility.xml
│ ├── accessory_filter.xml
│ └── c.xml
├── build.gradle
├── demo.gif
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── plugin
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── projection
│ │ └── car
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── projection
│ │ │ └── car
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── projection
│ └── car
│ └── ExampleUnitTest.java
├── screen_1.jpg
└── settings.gradle
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
15 |
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CarProjection
2 |
3 | 本项目基于carlife 协议投屏,安卓10以上无需root,可反向触控,仅用作研究学习
4 |
5 |
6 | # screenshot
7 | 
8 | 
9 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'com.google.protobuf'
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "28.0.3"
6 | defaultConfig {
7 | applicationId "com.projection.car"
8 | minSdkVersion 19
9 | targetSdkVersion 19
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled true
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | debug {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | sourceSets {
24 | main {
25 | // 定义proto文件目录
26 | java {
27 | srcDir 'src/main/java'
28 | }
29 | proto {
30 | srcDir 'src/main/proto'
31 | include '**/*.proto'
32 | }
33 | }
34 | }
35 |
36 | lintOptions {
37 | checkReleaseBuilds false
38 | // Or, if you prefer, you can continue to check for errors in release builds,
39 | // but continue the build even when errors are found:
40 | abortOnError false
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation fileTree(dir: 'libs', include: ['*.jar'])
46 | implementation "com.android.support:appcompat-v7:26.1.0"
47 | implementation "com.google.protobuf:protobuf-lite:3.0.0"
48 | // implementation 'com.google.protobuf:protobuf-java:4.0.0-rc-2'
49 | // implementation 'com.google.protobuf:protoc:4.0.0-rc-2'
50 | }
51 |
52 | protobuf {
53 | protoc {
54 | artifact = 'com.google.protobuf:protoc:3.0.0'
55 | }
56 | plugins {
57 | javalite {
58 | artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
59 | }
60 | }
61 | generateProtoTasks {
62 | all().each { task ->
63 | task.plugins {
64 | javalite {}
65 | }
66 | }
67 | }
68 | }
69 |
70 | //protobuf {
71 | // protoc {
72 | // artifact = 'com.google.protobuf:protoc:3.6.1'
73 | // }
74 | //
75 | // generateProtoTasks {
76 | // all().each { task ->
77 | // task.builtins {
78 | // remove java
79 | // }
80 | // task.builtins {
81 | // java {}
82 | // }
83 | // }
84 | // }
85 | //}
86 |
87 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/assets/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/assets/app-debug.apk
--------------------------------------------------------------------------------
/app/src/main/java/com/projection/car/ForgroundService.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.accessibilityservice.AccessibilityServiceInfo;
5 | import android.app.Notification;
6 | import android.app.NotificationChannel;
7 | import android.app.NotificationManager;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.support.v4.app.NotificationCompat;
11 | import android.view.accessibility.AccessibilityEvent;
12 |
13 | import static com.projection.car.Utils.log;
14 |
15 | public class ForgroundService extends AccessibilityService {
16 |
17 |
18 | public static ForgroundService mService;
19 |
20 | AccessibilityServiceInfo info;
21 |
22 | //初始化
23 | @Override
24 | protected void onServiceConnected() {
25 | super.onServiceConnected();
26 | log("gesture start");
27 | mService = this;
28 | }
29 |
30 | //实现辅助功能
31 | @Override
32 | public void onAccessibilityEvent(AccessibilityEvent event) {
33 |
34 |
35 | }
36 |
37 | @Override
38 | public void onInterrupt() {
39 | log("gesture onInterrupt");
40 | mService = null;
41 | }
42 |
43 | @Override
44 | public void onDestroy() {
45 | super.onDestroy();
46 | log("gesture onDestroy");
47 | mService = null;
48 | }
49 |
50 | @Override
51 | public void onCreate() {
52 | super.onCreate();
53 | log("car_projection start");
54 |
55 | }
56 |
57 | @Override
58 | public int onStartCommand(Intent intent, int flags, int startId) {
59 | if (intent != null && intent.getAction() != null) {
60 | log("action is " + intent.getAction());
61 | if ("service_start".equals(intent.getAction())) {
62 | NotificationChannel channel = null;
63 | final String channelId = "projection_car";
64 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
65 | channel = new NotificationChannel(channelId, "System", NotificationManager.IMPORTANCE_LOW);
66 | NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
67 | if (manager != null) {
68 | manager.createNotificationChannel(channel);
69 | }
70 | }
71 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
72 | final Notification xiriNotification = notificationBuilder.setOngoing(true)
73 | .setContentText("projection car")
74 | .setWhen(System.currentTimeMillis())
75 | .setSmallIcon(R.drawable.ic_launcher_background)
76 | .build();
77 | startForeground(10010, xiriNotification);
78 | } else {
79 | stopForeground(true);
80 | }
81 |
82 | }
83 | return super.onStartCommand(intent, flags, startId);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/projection/car/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.content.SharedPreferences;
10 | import android.hardware.usb.UsbAccessory;
11 | import android.hardware.usb.UsbManager;
12 | import android.os.Build;
13 | import android.os.Bundle;
14 | import android.os.ParcelFileDescriptor;
15 | import android.os.PowerManager;
16 | import android.provider.Settings;
17 | import android.support.v7.app.AlertDialog;
18 | import android.support.v7.app.AppCompatActivity;
19 | import android.text.TextUtils;
20 | import android.view.View;
21 | import android.widget.EditText;
22 | import android.widget.TextView;
23 |
24 | import java.io.FileDescriptor;
25 | import java.io.FileInputStream;
26 | import java.io.FileOutputStream;
27 | import java.io.IOException;
28 | import java.io.InputStream;
29 | import java.util.ArrayList;
30 |
31 | import static com.projection.car.Utils.REQUEST_CODE;
32 | import static com.projection.car.Utils.getRootAhth;
33 | import static com.projection.car.Utils.log;
34 | import static com.projection.car.Utils.logAll;
35 | import static com.projection.car.Utils.pauseSong;
36 |
37 | public class MainActivity extends AppCompatActivity {
38 |
39 |
40 | private static final String ACTION_USB_PERMISSION = "org.ammlab.android.app.helloadk.action.USB_PERMISSION";
41 |
42 |
43 | private Context mContext;
44 |
45 | private UsbManager mUsbManager;
46 | private UsbAccessory mUsbAccessory;
47 | private ParcelFileDescriptor mFileDescriptor;
48 |
49 |
50 | private int mVideoBit = 0;
51 | private int mVideoFrame = 0;
52 |
53 | private PowerManager.WakeLock mWakeLock;
54 |
55 | private MsgProcess mMsgProcess;
56 |
57 |
58 | private TextView mLog;
59 | private EditText bitTxt, frameTxt;
60 | private TextView wTxt, hTxt, serialTxt;
61 |
62 |
63 | private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
64 |
65 | @Override
66 | public void onReceive(Context context, Intent intent) {
67 | String action = intent.getAction();
68 |
69 | log("receive accessory_filter connect broadcast:" + action);
70 |
71 | if (ACTION_USB_PERMISSION.equals(action)) {
72 | synchronized (this) {
73 | UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
74 |
75 | //获取accessory句柄成功
76 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
77 | log("prepare to open accessory_filter stream");
78 |
79 | mUsbAccessory = accessory;
80 | openAccessory(mUsbAccessory);
81 |
82 | } else {
83 | log("permission denied for accessory " + accessory);
84 |
85 | mUsbAccessory = null;
86 |
87 | }
88 | }
89 | } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
90 |
91 | Intent stopintent = new Intent(mContext, ForgroundService.class);
92 | stopintent.setAction("service_stop");
93 | mContext.startService(stopintent);
94 |
95 | if (mWakeLock.isHeld()) {
96 | mWakeLock.release();
97 | }
98 |
99 | UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
100 | log("USB_ACCESSORY_DETACHED " + accessory);
101 | mUsbAccessory = null;
102 |
103 | mMsgProcess.resetUsb();
104 |
105 | System.exit(0);
106 |
107 | } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
108 | UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
109 | openAccessory(mUsbAccessory);
110 | //检测到us连接
111 | log("USB_ACCESSORY_ATTACHED " + accessory);
112 | }
113 | }
114 | };
115 |
116 | @Override
117 | protected void onCreate(Bundle savedInstanceState) {
118 | super.onCreate(savedInstanceState);
119 | setContentView(R.layout.activity_main);
120 | mContext = this;
121 |
122 | checkPermission();
123 | if(!getRootAhth()){
124 | checkAccessibilitySettingsOn(mContext, ForgroundService.class.getCanonicalName());
125 | }
126 |
127 | findViewById(R.id.set_plugin1).setOnClickListener(new View.OnClickListener() {
128 | @Override
129 | public void onClick(View v) {
130 | try {
131 | InputStream inputStream = getAssets().open("app-debug.apk");
132 | byte [] readb = new byte[1024];
133 | String p = "/sdcard/plugin.apk";
134 | FileOutputStream fileOutputStream = new FileOutputStream(p);
135 | int len = 0;
136 | while ((len = inputStream.read(readb)) > 0){
137 | fileOutputStream.write(readb,0,len);
138 | }
139 | if(Utils.installPlugin(p)){
140 | log("install ok");
141 | }
142 |
143 | } catch (IOException e) {
144 | e.printStackTrace();
145 | }
146 | }
147 | });
148 |
149 |
150 | findViewById(R.id.set_plugin2).setOnClickListener(new View.OnClickListener() {
151 | @Override
152 | public void onClick(View v) {
153 |
154 | }
155 | });
156 |
157 | findViewById(R.id.delete_plugin).setOnClickListener(new View.OnClickListener() {
158 | @Override
159 | public void onClick(View v) {
160 | Utils.deletPlugin();
161 | }
162 | });
163 |
164 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
165 | mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, Utils.TAG);
166 |
167 |
168 | mLog = findViewById(R.id.log);
169 | bitTxt = findViewById(R.id.bit);
170 | frameTxt = findViewById(R.id.frame);
171 | wTxt = findViewById(R.id.w);
172 | hTxt = findViewById(R.id.h);
173 | serialTxt = findViewById(R.id.serial);
174 |
175 | final SharedPreferences sharedPreferences = getSharedPreferences("set", MODE_PRIVATE);
176 | mVideoBit = sharedPreferences.getInt("bit", 30);
177 | mVideoFrame = sharedPreferences.getInt("frame", 3000000);
178 | bitTxt.setText(mVideoBit + "");
179 | frameTxt.setText(mVideoFrame + "");
180 |
181 | findViewById(R.id.config).setOnClickListener(new View.OnClickListener() {
182 | @Override
183 | public void onClick(View v) {
184 | String bit = bitTxt.getText().toString();
185 | String frame = frameTxt.getText().toString();
186 | sharedPreferences.edit().putInt("bit", Integer.parseInt(bit)).commit();
187 | sharedPreferences.edit().putInt("frame", Integer.parseInt(frame)).commit();
188 | Utils.installPlugin("");
189 |
190 |
191 | }
192 | });
193 | mMsgProcess = new MsgProcess(this, mVideoBit, mVideoFrame, new MsgProcess.InfoListener() {
194 | @Override
195 | public void onVISSize(int x, int y) {
196 | wTxt.setText("车机屏幕宽度" + x);
197 | hTxt.setText("车机屏幕高度" + y);
198 | }
199 |
200 | @Override
201 | public void onVISID(String id) {
202 | serialTxt.setText("车机id :" + id);
203 | }
204 | });
205 |
206 |
207 | IntentFilter filter = new IntentFilter();
208 | filter.addAction(ACTION_USB_PERMISSION);
209 | filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
210 | filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
211 | filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
212 | // filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
213 | mContext.registerReceiver(mUsbReceiver, filter);
214 | mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
215 |
216 |
217 | checkUSBDevice();
218 | logAll();
219 |
220 | }
221 |
222 | protected void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent) {
223 | if (paramInt1 == REQUEST_CODE) {
224 |
225 | mMsgProcess.mediaPermissionOk(this, paramInt2, paramIntent);
226 | }
227 | mMsgProcess.startReadAudio();
228 | }
229 |
230 |
231 | @Override
232 | protected void onDestroy() {
233 | super.onDestroy();
234 | mContext.unregisterReceiver(mUsbReceiver);
235 | }
236 |
237 |
238 | private void openAccessory(UsbAccessory accessory) {
239 | log("openAccessory");
240 | mFileDescriptor = mUsbManager.openAccessory(accessory);
241 |
242 | if (mFileDescriptor != null) {
243 | FileDescriptor fd = mFileDescriptor.getFileDescriptor();
244 | log("now usb fd" + fd);
245 | if (fd != null) {
246 | FileInputStream mInputStream = new FileInputStream(fd);
247 | log("accessory opened DataTranPrepared");
248 | FileOutputStream mOutputStream = new FileOutputStream(fd);
249 |
250 | mMsgProcess.startProjection(mInputStream, mOutputStream);
251 |
252 | mWakeLock.acquire();//保持屏幕唤醒
253 |
254 |
255 | Intent intent = new Intent(this, ForgroundService.class);
256 | intent.setAction("service_start");
257 | startService(intent);
258 | }
259 |
260 | log("accessory opened");
261 | } else {
262 | log("accessory open fail");
263 | }
264 | }
265 |
266 | private void checkUSBDevice() {
267 | log("checkUSBDevice");
268 | UsbAccessory[] accessories = mUsbManager.getAccessoryList();
269 |
270 | if (accessories == null) {
271 | log("accessories list is null");
272 | return;
273 | }
274 |
275 | log("accessories length " + accessories.length);
276 |
277 | UsbAccessory accessory = accessories[0];
278 | if (accessory != null) {
279 | log("accessories not null");
280 | if (mUsbManager.hasPermission(accessory)) {
281 |
282 | mUsbAccessory = accessory;
283 | openAccessory(mUsbAccessory);
284 | } else {
285 | log("accessories null per");
286 | PendingIntent mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
287 | mUsbManager.requestPermission(accessory, mPermissionIntent);
288 | }
289 | } else {
290 | log("accessories null");
291 | }
292 | }
293 |
294 |
295 | private boolean checkAccessibilitySettingsOn(Context mContext, String serviceName) {
296 | int accessibilityEnabled = 0;
297 | // 对应的服务
298 | final String service = getPackageName() + "/" + serviceName;
299 | try {
300 | accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),
301 | android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
302 | log("accessibilityEnabled = " + accessibilityEnabled);
303 | } catch (Settings.SettingNotFoundException e) {
304 | log("Error finding setting, default accessibility to not found: " + e.getMessage());
305 | }
306 |
307 | TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
308 |
309 | if (accessibilityEnabled == 1) {
310 | log("***ACCESSIBILITY IS ENABLED*** -----------------");
311 |
312 | String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),
313 | Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
314 | if (settingValue != null) {
315 | mStringColonSplitter.setString(settingValue);
316 | while (mStringColonSplitter.hasNext()) {
317 | String accessibilityService = mStringColonSplitter.next();
318 | log("-------------- > accessibilityService :: " + accessibilityService + " " + service);
319 | if (accessibilityService.equalsIgnoreCase(service)) {
320 | log("We've found the correct setting - accessibility is switched on!");
321 | return true;
322 | }
323 | }
324 | }
325 | } else {
326 | log("***ACCESSIBILITY IS DISABLED***");
327 | }
328 | //跳转设置打开无障碍
329 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.Theme_AppCompat_DayNight_Dialog);
330 | builder.setTitle("权限申请");
331 | builder.setMessage("应用需要开启辅助功能,如果取消部分功能不可用");
332 | builder.setPositiveButton("确定开启", new DialogInterface.OnClickListener() {
333 | @Override
334 | public void onClick(DialogInterface dialog, int which) {
335 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
336 | startActivity(intent);
337 | }
338 | });
339 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
340 | @Override
341 | public void onClick(DialogInterface dialog, int which) {
342 |
343 | }
344 | });
345 |
346 | builder.setCancelable(false);
347 | builder.show();
348 |
349 | return false;
350 | }
351 |
352 | private void checkPermission() {
353 | if (Build.VERSION.SDK_INT < 21) {
354 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.Theme_AppCompat_DayNight_Dialog);
355 | // builder.setTitle("权限申请");
356 | builder.setMessage("应用需要android 5.1 版本以上运行");
357 | // builder.setPositiveButton("退出", new DialogInterface.OnClickListener() {
358 | // @Override
359 | // public void onClick(DialogInterface dialog, int which) {
360 | // finish();
361 | // }
362 | // });
363 | builder.setNegativeButton("退出", new DialogInterface.OnClickListener() {
364 | @Override
365 | public void onClick(DialogInterface dialog, int which) {
366 | finish();
367 | }
368 | });
369 |
370 | builder.setCancelable(false);
371 | builder.show();
372 | }
373 |
374 | if (Build.VERSION.SDK_INT < 24) {
375 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.Theme_AppCompat_DayNight_Dialog);
376 | // builder.setTitle("权限申请");
377 | builder.setMessage("车机反控功能需要android 7.0版本及以上");
378 | builder.setPositiveButton("了解", new DialogInterface.OnClickListener() {
379 | @Override
380 | public void onClick(DialogInterface dialog, int which) {
381 |
382 | }
383 | });
384 | builder.setNegativeButton("退出", new DialogInterface.OnClickListener() {
385 | @Override
386 | public void onClick(DialogInterface dialog, int which) {
387 | finish();
388 | }
389 | });
390 |
391 | builder.setCancelable(false);
392 | builder.show();
393 | }
394 | }
395 |
396 | }
397 |
--------------------------------------------------------------------------------
/app/src/main/java/com/projection/car/MediaCodecTool.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.hardware.display.DisplayManager;
7 | import android.hardware.display.VirtualDisplay;
8 | import android.media.AudioFormat;
9 | import android.media.AudioPlaybackCaptureConfiguration;
10 | import android.media.AudioRecord;
11 | import android.media.MediaCodec;
12 | import android.media.MediaCodecInfo;
13 | import android.media.MediaFormat;
14 | import android.media.MediaRecorder;
15 | import android.media.projection.MediaProjection;
16 | import android.media.projection.MediaProjection.Callback;
17 | import android.media.projection.MediaProjectionManager;
18 | import android.os.Build;
19 | import android.support.annotation.NonNull;
20 | import android.util.Log;
21 |
22 | import java.io.FileNotFoundException;
23 | import java.io.FileOutputStream;
24 | import java.nio.ByteBuffer;
25 | import java.util.Arrays;
26 |
27 | import static com.projection.car.Utils.log;
28 |
29 |
30 | public class MediaCodecTool {
31 |
32 | private static final String SCREENCAP_NAME = "screencap";
33 | private static final String TAG = MediaCodecTool.class.getName();
34 |
35 | private MediaProjection sMediaProjection;
36 | private MediaProjectionManager mProjectionManager;
37 | private VirtualDisplay mVirtualDisplay;
38 |
39 | private MediaCodec mMediaCodec;
40 | private boolean mFirstConfigFrame;
41 | private byte[] mConfigByte;
42 | private VideoDataEncodeListener mEncodeCall;
43 |
44 | private int mDensity;
45 | private int mWidth;
46 | private int mHeight;
47 | private int mBit, mFrame;
48 |
49 | private boolean testAudio = false;
50 | private FileOutputStream mOutputStream;
51 |
52 | MediaCodecTool() {
53 |
54 | }
55 |
56 | private void createVirtualDisplay() {
57 | if (testAudio) {
58 | try {
59 | mOutputStream = new FileOutputStream("/sdcard/test.mp4");
60 | } catch (FileNotFoundException e) {
61 | e.printStackTrace();
62 | }
63 | }
64 |
65 |
66 | log("VIS w = " + mWidth + ", h = " + mHeight);
67 | try {
68 | mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
69 | MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
70 | mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mFrame);//4000000
71 | mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mBit);//10
72 | mediaFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, mBit);//10
73 | mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
74 | mediaFormat.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 100000L);
75 | mediaFormat.setLong(MediaFormat.KEY_DURATION, 100000L);
76 | mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);//2130706433
77 | // mediaFormat.setInteger("profile", 1);
78 | // mediaFormat.setInteger("level", 256);
79 | mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
80 |
81 | mVirtualDisplay = sMediaProjection.createVirtualDisplay(SCREENCAP_NAME, mWidth, mHeight, mDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mMediaCodec.createInputSurface(), null, null);
82 |
83 |
84 | mMediaCodec.setCallback(new MediaCodec.Callback() {
85 | @Override
86 | public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
87 |
88 | }
89 |
90 | @Override
91 | public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo bufferInfo) {
92 | try {
93 | ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(index);
94 | byte[] outData = new byte[bufferInfo.size];
95 | outputBuffer.get(outData);
96 | // flags 利用位操作,定义的 flag 都是 2 的倍数
97 | if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // 配置相关的内容,也就是 SPS,PPS
98 | if (testAudio) {
99 | mOutputStream.write(outData, 0, outData.length);
100 | mOutputStream.flush();
101 | }
102 |
103 |
104 | mConfigByte = new byte[outData.length];
105 | mFirstConfigFrame = true;
106 | System.arraycopy(outData, 0, mConfigByte, 0, outData.length);
107 | Log.e(TAG, "now CONFIG is" + Arrays.toString(mConfigByte));
108 | } else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // 关键帧
109 | if (testAudio) {
110 | mOutputStream.write(outData, 0, outData.length);
111 | mOutputStream.flush();
112 | }
113 | if (mEncodeCall != null) {
114 | Log.e(TAG, "now frame is" + outData.length);
115 | if (mFirstConfigFrame) {
116 | mFirstConfigFrame = false;
117 | byte[] t = new byte[mConfigByte.length + outData.length];
118 | System.arraycopy(mConfigByte, 0, t, 0, mConfigByte.length);
119 | System.arraycopy(outData, 0, t, mConfigByte.length, outData.length);
120 | outData = t;
121 | }
122 |
123 | mEncodeCall.onData(outData);
124 | }
125 | } else {
126 | // 非关键帧和SPS、PPS,直接写入文件,可能是B帧或者P帧
127 | if (testAudio) {
128 | mOutputStream.write(outData, 0, outData.length);
129 | mOutputStream.flush();
130 | }
131 |
132 | if (mEncodeCall != null) {
133 | mEncodeCall.onData(outData);
134 | }
135 | }
136 | mMediaCodec.releaseOutputBuffer(index, false);
137 | } catch (Exception e) {
138 | e.printStackTrace();
139 | }
140 |
141 | }
142 |
143 | @Override
144 | public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
145 |
146 | }
147 |
148 | @Override
149 | public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
150 |
151 | }
152 | });
153 | mMediaCodec.start();
154 |
155 |
156 | // MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
157 | // while (true) {
158 | // int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
159 | // // 从输出缓冲区队列中拿到编码好的内容,对内容进行相应处理后在释放
160 | // while (outputBufferIndex >= 0) {
161 | // Log.e(TAG, "outputBufferIndex " + outputBufferIndex);
162 | // ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
163 | // ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
164 | // byte[] outData = new byte[bufferInfo.size];
165 | // outputBuffer.get(outData);
166 | // // flags 利用位操作,定义的 flag 都是 2 的倍数
167 | // if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { // 配置相关的内容,也就是 SPS,PPS
168 | //// mOutputStream.write(outData, 0, outData.length);
169 | //// mOutputStream.flush();
170 | //
171 | // mConfigByte = new byte[outData.length];
172 | // mFirstConfigFrame = true;
173 | // System.arraycopy(outData,0,mConfigByte,0,outData.length);
174 | // Log.e(TAG,"now CONFIG is" + Arrays.toString(mConfigByte));
175 | // } else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { // 关键帧
176 | //// mOutputStream.write(outData, 0, outData.length);
177 | //// mOutputStream.flush();
178 | // if(mEncodeCall != null){
179 | // Log.e(TAG,"now frame is" + outData.length);
180 | // if(mFirstConfigFrame){
181 | // mFirstConfigFrame = false;
182 | // byte[] t = new byte[mConfigByte.length + outData.length];
183 | // System.arraycopy(mConfigByte,0,t,0,mConfigByte.length);
184 | // System.arraycopy(outData,0,t,mConfigByte.length,outData.length);
185 | // outData = t;
186 | // }
187 | //
188 | // mEncodeCall.onData(outData);
189 | // }
190 | // } else {
191 | // // 非关键帧和SPS、PPS,直接写入文件,可能是B帧或者P帧
192 | //// mOutputStream.write(outData, 0, outData.length);
193 | //// mOutputStream.flush();
194 | // if(mEncodeCall != null){
195 | // mEncodeCall.onData(outData);
196 | // }
197 | // }
198 | // mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
199 | // outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
200 | // }
201 | // }
202 |
203 |
204 | } catch (Exception localException) {
205 | localException.printStackTrace();
206 | }
207 | }
208 |
209 | public MediaProjection getMediaProjection(){
210 | return sMediaProjection;
211 | }
212 |
213 |
214 | public void startProjection(Activity context, VideoDataEncodeListener encodeCall, int code,float w, float h, int bit, int frame) {
215 | mEncodeCall = encodeCall;
216 | mBit = bit;
217 | mFrame = frame;
218 | mWidth = (int) w;
219 | mHeight = (int) h;
220 | mProjectionManager = ((MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE));
221 | context.startActivityForResult(mProjectionManager.createScreenCaptureIntent(), code);
222 | }
223 |
224 | public void stopProjection() {
225 | if (sMediaProjection != null) {
226 | try {
227 | sMediaProjection.stop();
228 | } catch (Exception e) {
229 | e.printStackTrace();
230 | }
231 | }
232 | }
233 |
234 | public void onActivityResult(Activity activity, int paramInt2, Intent paramIntent) {
235 | sMediaProjection = mProjectionManager.getMediaProjection(paramInt2, paramIntent);
236 | if (sMediaProjection != null) {
237 | mDensity = activity.getResources().getDisplayMetrics().densityDpi;
238 | createVirtualDisplay();
239 | sMediaProjection.registerCallback(new MediaProjectionStopCallback(), null);
240 | }
241 |
242 | }
243 |
244 |
245 | private class MediaProjectionStopCallback extends Callback {
246 | private MediaProjectionStopCallback() {
247 | }
248 |
249 | public void onStop() {
250 |
251 | Log.e(TAG, "stopping projection.");
252 |
253 | mMediaCodec.stop();
254 |
255 | if (mVirtualDisplay != null) {
256 | mVirtualDisplay.release();
257 | }
258 | sMediaProjection.unregisterCallback(MediaCodecTool.MediaProjectionStopCallback.this);
259 | }
260 | }
261 |
262 |
263 | public interface VideoDataEncodeListener {
264 | void onData(byte[] data);
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/app/src/main/java/com/projection/car/MsgProcess.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.accessibilityservice.GestureDescription;
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.SharedPreferences;
9 | import android.content.pm.ApplicationInfo;
10 | import android.graphics.Path;
11 | import android.media.AudioAttributes;
12 | import android.media.AudioFormat;
13 | import android.media.AudioPlaybackCaptureConfiguration;
14 | import android.media.AudioRecord;
15 | import android.media.MediaRecorder;
16 | import android.os.Build;
17 | import android.os.Handler;
18 | import android.os.HandlerThread;
19 | import android.os.Looper;
20 | import android.os.Message;
21 | import android.os.SystemClock;
22 | import android.support.annotation.NonNull;
23 | import android.util.DisplayMetrics;
24 | import android.view.Surface;
25 | import android.view.WindowManager;
26 | import android.widget.Toast;
27 |
28 | import com.baidu.carlife.protobuf.CarlifeCarHardKeyCodeProto;
29 | import com.baidu.carlife.protobuf.CarlifeMusicInitProto;
30 | import com.baidu.carlife.protobuf.CarlifeTouchActionProto;
31 | import com.example.car.CarlifeAuthenResultProto;
32 | import com.example.car.CarlifeDeviceInfoProto;
33 | import com.example.car.CarlifeProtocolVersionMatchStatusProto;
34 | import com.example.car.CarlifeStatisticsInfoProto;
35 | import com.example.car.CarlifeVideoEncoderInfoProto;
36 | import com.google.protobuf.InvalidProtocolBufferException;
37 |
38 | import java.io.FileInputStream;
39 | import java.io.FileNotFoundException;
40 | import java.io.FileOutputStream;
41 | import java.io.IOException;
42 | import java.util.ArrayList;
43 | import java.util.Arrays;
44 |
45 | import static android.content.Context.MODE_PRIVATE;
46 | import static com.projection.car.Utils.ACTION_DOWN;
47 | import static com.projection.car.Utils.ACTION_MOVE;
48 | import static com.projection.car.Utils.ACTION_UP;
49 | import static com.projection.car.Utils.CMD;
50 | import static com.projection.car.Utils.KEYCODE_SEEK_ADD;
51 | import static com.projection.car.Utils.KEYCODE_SEEK_SUB;
52 | import static com.projection.car.Utils.MEDIA;
53 | import static com.projection.car.Utils.MSG_CMD_FOREGROUND;
54 | import static com.projection.car.Utils.MSG_CMD_HU_INFO;
55 | import static com.projection.car.Utils.MSG_CMD_HU_PROTOCOL_VERSION;
56 | import static com.projection.car.Utils.MSG_CMD_MD_AUTHEN_RESULT;
57 | import static com.projection.car.Utils.MSG_CMD_MD_INFO;
58 | import static com.projection.car.Utils.MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS;
59 | import static com.projection.car.Utils.MSG_CMD_STATISTIC_INFO;
60 | import static com.projection.car.Utils.MSG_CMD_VIDEO_ENCODER_INIT;
61 | import static com.projection.car.Utils.MSG_CMD_VIDEO_ENCODER_INIT_DONE;
62 | import static com.projection.car.Utils.MSG_CMD_VIDEO_ENCODER_START;
63 | import static com.projection.car.Utils.MSG_MEDIA_DATA;
64 | import static com.projection.car.Utils.MSG_MEDIA_INIT;
65 | import static com.projection.car.Utils.MSG_TOUCH_ACTION;
66 | import static com.projection.car.Utils.MSG_TOUCH_CAR_HARD_KEY_CODE;
67 | import static com.projection.car.Utils.MSG_VIDEO_DATA;
68 | import static com.projection.car.Utils.MSG_WRITE_AUDIO;
69 | import static com.projection.car.Utils.MSG_WRITE_VIDEO;
70 | import static com.projection.car.Utils.REQUEST_CODE;
71 | import static com.projection.car.Utils.TOUCH;
72 | import static com.projection.car.Utils.VIDEO;
73 | import static com.projection.car.Utils.bytesToInt2;
74 | import static com.projection.car.Utils.bytesToShort2;
75 | import static com.projection.car.Utils.exportCMDMsg;
76 | import static com.projection.car.Utils.exportVideoMsg;
77 | import static com.projection.car.Utils.intToBytes2;
78 | import static com.projection.car.Utils.log;
79 | import static com.projection.car.Utils.nextSong;
80 | import static com.projection.car.Utils.previosSong;
81 |
82 | public class MsgProcess {
83 |
84 | private boolean testAduio = false;
85 |
86 |
87 | private volatile boolean usbOk;
88 | private FileInputStream mInputStream;
89 | private FileOutputStream mOutputStream;
90 | private Activity mContext;
91 |
92 |
93 | private Handler mUsbReadHandler;
94 | private Handler mUsbWriteHandler;
95 |
96 |
97 | private AudioHandler mAudioReadHandler;
98 |
99 | private MediaCodecTool mMediaCodecTool;
100 |
101 | private Path mGesturePath = new Path();
102 | private int mGestureMoveCount = 0;
103 | private ArrayList mGestureMoveArray = new ArrayList<>();
104 | private long mGestureStartTime = 0;
105 |
106 | private float mVISWidth = 1280;
107 | private float mVISHeight = 720;
108 | private float mMobileWidth = 1920;
109 | private float mMobileHeight = 1080;
110 | private float mPortraitScreenVISGestureFactorW = 1.0f;
111 | private float mPortraitScreenVISGestureFactorH = 1.0f;
112 | private float mLandscapeScreenVISGestureFactorW = 1.0f;
113 | private float mLandscapeScreenVISGestureFactorH = 1.0f;
114 |
115 | private float mLeft_x;
116 | private Handler mMainHandler = new Handler();
117 |
118 | private int mVideoBit = 0;
119 | private int mVideoFrame = 0;
120 | private InfoListener mInfoListener;
121 |
122 | MsgProcess(Activity context, int bit, int frame, InfoListener infoListener) {
123 |
124 | mContext = context;
125 | mInfoListener = infoListener;
126 | mVideoBit = bit;
127 | mVideoFrame = frame;
128 |
129 | refreshSize();
130 |
131 | mMediaCodecTool = new MediaCodecTool();
132 |
133 | HandlerThread audioThread = new HandlerThread("audio");
134 | audioThread.start();
135 | mAudioReadHandler = new AudioHandler(audioThread.getLooper());
136 |
137 |
138 | startUsbTransferThread();
139 |
140 | if (testAduio) {
141 | mMediaCodecTool.startProjection(mContext, videoDataEncodeListener, REQUEST_CODE, mVISWidth, mVISHeight, mVideoBit, mVideoFrame);
142 | mAudioReadHandler.sendEmptyMessage(AudioHandler.AUDIO_START);
143 | }
144 | }
145 |
146 | private Runnable runnable = new Runnable() {
147 | @Override
148 | public void run() {
149 |
150 | mInputStream = null;
151 | mOutputStream = null;
152 | }
153 | };
154 |
155 | private Runnable runnable_toast = new Runnable() {
156 | @Override
157 | public void run() {
158 |
159 | Toast.makeText(mContext, "当前版本未授权,稍后自动断连", Toast.LENGTH_LONG).show();
160 | }
161 | };
162 |
163 | public void startProjection(FileInputStream in, FileOutputStream out) {
164 | log("startProjection");
165 | usbOk = true;
166 | mInputStream = in;
167 | mOutputStream = out;
168 | mUsbReadHandler.sendEmptyMessage(0);
169 |
170 | }
171 |
172 | public void mediaPermissionOk(Activity activity, int paramInt2, Intent paramIntent) {
173 | mMediaCodecTool.onActivityResult(activity, paramInt2, paramIntent);
174 | }
175 |
176 | public synchronized void resetUsb() {
177 | if (usbOk) {
178 | log("resetUsb");
179 | usbOk = false;
180 | mAudioReadHandler.sendEmptyMessage(AudioHandler.AUDIO_STOP);
181 | mMediaCodecTool.stopProjection();
182 | mUsbWriteHandler.removeCallbacksAndMessages(null);
183 | }
184 |
185 | }
186 |
187 | public void startReadAudio() {
188 | mAudioReadHandler.sendEmptyMessage(AudioHandler.AUDIO_START);
189 | }
190 |
191 |
192 | private MediaCodecTool.VideoDataEncodeListener videoDataEncodeListener = new MediaCodecTool.VideoDataEncodeListener() {
193 | @Override
194 | public void onData(byte[] data) {
195 | try {
196 | // log("data len = " + data.length);
197 | byte[] carLifeMsg = exportVideoMsg(MSG_VIDEO_DATA, data);
198 | byte[] headmsg = new byte[8];
199 | headmsg[3] = VIDEO;
200 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
201 | CarMsg carMsg = new CarMsg(headmsg, carLifeMsg);
202 | mUsbWriteHandler.obtainMessage(MSG_WRITE_VIDEO, carMsg).sendToTarget();
203 | } catch (Exception e) {
204 | e.printStackTrace();
205 | }
206 |
207 | }
208 | };
209 |
210 | private void refreshSize() {
211 |
212 | mMainHandler.post(new Runnable() {
213 | @Override
214 | public void run() {
215 | mInfoListener.onVISSize((int) mVISWidth, (int) mVISHeight);
216 |
217 | }
218 | });
219 |
220 |
221 | DisplayMetrics metrics = new DisplayMetrics();
222 | WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
223 | manager.getDefaultDisplay().getRealMetrics(metrics);
224 | log("www = " + metrics.widthPixels + " hhh = " + metrics.heightPixels);
225 | mMobileWidth = metrics.widthPixels;
226 | mMobileHeight = metrics.heightPixels;
227 | final SharedPreferences sharedPreferences = mContext.getSharedPreferences("set", MODE_PRIVATE);
228 | mMobileWidth = sharedPreferences.getFloat("mobile_w", (float) mMobileWidth);
229 | mMobileHeight = sharedPreferences.getFloat("mobile_h", (float) mMobileHeight);
230 | mLandscapeScreenVISGestureFactorW = mMobileWidth / mVISWidth;
231 | mLandscapeScreenVISGestureFactorH = mMobileHeight / mVISHeight;
232 |
233 | float portrixScrennWidth = mVISWidth * mVISHeight / mMobileWidth;// 车机竖屏的实际宽 用车机的高做投屏的高,保持比例
234 | mPortraitScreenVISGestureFactorW = mMobileHeight / (portrixScrennWidth);//竖屏下宽带除车机投屏实际屏幕宽度
235 | mPortraitScreenVISGestureFactorH = mMobileWidth / mVISHeight;
236 |
237 | mLeft_x = (mVISWidth - portrixScrennWidth) / 2.0f; //界面偏移值
238 | log("refreshSize w " + mMobileWidth + " h = " + mMobileHeight + ", mVISWidth " + mVISWidth + "mVISHeight" + mVISHeight + ", mirror = " + mPortraitScreenVISGestureFactorW + ", " + mPortraitScreenVISGestureFactorH +
239 | mLandscapeScreenVISGestureFactorW + ", " + mLandscapeScreenVISGestureFactorH + ", leftx " + mLeft_x);
240 | }
241 |
242 | private void genarateGesture(int type, float g_x, float g_y) {
243 | float x = 0;
244 | float y = 0;
245 | if (type == ACTION_DOWN) {
246 | mGestureStartTime = SystemClock.elapsedRealtime();
247 | mGesturePath.reset();
248 | mGestureMoveArray.clear();
249 | mGestureMoveCount = 0;
250 | int angle = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
251 |
252 | if (angle == Surface.ROTATION_0) {
253 | x = (g_x - mLeft_x) * mPortraitScreenVISGestureFactorW;
254 | y = g_y * mPortraitScreenVISGestureFactorH;
255 |
256 | } else if (angle == Surface.ROTATION_90) {
257 | x = g_x * mLandscapeScreenVISGestureFactorW;
258 | y = g_y * mLandscapeScreenVISGestureFactorH;
259 | }
260 | log("now moveTo x = " + x + ", " + y);
261 | mGesturePath.moveTo(x, y);
262 | mGestureMoveArray.add(x);
263 | mGestureMoveArray.add(y);
264 |
265 | } else if (type == ACTION_UP) {
266 |
267 | long gestureTime = 30;
268 | if (mGestureMoveCount > 2) {
269 | gestureTime = SystemClock.elapsedRealtime() - mGestureStartTime;
270 | gestureTime = gestureTime > 300 ? 300 : gestureTime;
271 | }
272 |
273 | log("now dispatchGesture time is " + gestureTime);
274 |
275 | Utils.touch(mGestureMoveArray, gestureTime);
276 |
277 | GestureDescription.StrokeDescription sd = new GestureDescription.StrokeDescription(mGesturePath, 0, gestureTime);
278 |
279 | if (ForgroundService.mService != null) {
280 | ForgroundService.mService.dispatchGesture(new GestureDescription.Builder().addStroke(sd).build(), new AccessibilityService.GestureResultCallback() {
281 | @Override
282 | public void onCompleted(GestureDescription gestureDescription) {
283 | super.onCompleted(gestureDescription);
284 | log("now dispatchGesture ok");
285 | }
286 |
287 | @Override
288 | public void onCancelled(GestureDescription gestureDescription) {
289 | super.onCancelled(gestureDescription);
290 | log("now dispatchGesture cancle");
291 | }
292 | }, null);
293 | }
294 | } else if (type == ACTION_MOVE) {
295 | int angle = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
296 | if (angle == Surface.ROTATION_0 || angle == Surface.ROTATION_180) {
297 | x = (g_x - mLeft_x) * mPortraitScreenVISGestureFactorW;
298 | y = g_y * mPortraitScreenVISGestureFactorH;
299 |
300 | } else if (angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270) {
301 | x = g_x * mLandscapeScreenVISGestureFactorW;
302 | y = g_y * mLandscapeScreenVISGestureFactorH;
303 | }
304 | log("now lineTo x = " + x + ", " + y);
305 | mGesturePath.lineTo(x, y);
306 | mGestureMoveArray.add(x);
307 | mGestureMoveArray.add(y);
308 | mGestureMoveCount++;
309 | }
310 |
311 | }
312 |
313 |
314 | class AudioHandler extends Handler {
315 |
316 | public static final int AUDIO_START = 0;
317 | public static final int AUDIO_READ = 1;
318 | public static final int AUDIO_STOP = 3;
319 |
320 | private AudioRecord mAudioRecord;
321 | private boolean mAudioStart;
322 | private FileOutputStream fileOutputStream;
323 |
324 |
325 | public AudioHandler(Looper looper) {
326 | super(looper);
327 | }
328 |
329 | @Override
330 | public void handleMessage(@NonNull Message msg) {
331 | super.handleMessage(msg);
332 | switch (msg.what) {
333 | case AUDIO_START:
334 | try {
335 | if (mAudioStart) {
336 | break;
337 | }
338 | if (testAduio) {
339 | try {
340 | fileOutputStream = new FileOutputStream("/sdcard/remix.pcm");
341 | } catch (FileNotFoundException e) {
342 | e.printStackTrace();
343 | }
344 | }
345 | int sample = 48000;
346 |
347 | // if(!(isSystemApp(mContext) || isSystemUpdateApp(mContext)) && Build.VERSION.SDK_INT >= 29) {
348 |
349 | if (Build.VERSION.SDK_INT >= 23) {
350 |
351 | try {
352 | AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration.Builder(mMediaCodecTool.getMediaProjection())
353 | .excludeUsage(AudioAttributes.USAGE_NOTIFICATION)
354 | .build();
355 | int minBufferSize = AudioRecord.getMinBufferSize(sample, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
356 | mAudioRecord = new AudioRecord.Builder()
357 | .setAudioPlaybackCaptureConfig(config)
358 | .setAudioFormat(new AudioFormat.Builder()
359 | .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
360 | .setSampleRate(sample)
361 | .setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
362 | .build())
363 | .setBufferSizeInBytes(minBufferSize)
364 | .build();
365 | } catch (Exception e) {
366 | e.printStackTrace();
367 | }
368 |
369 | } else {
370 | int minBufferSize = AudioRecord.getMinBufferSize(sample, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
371 | mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.REMOTE_SUBMIX, sample, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize);
372 |
373 | }
374 |
375 | mAudioRecord.startRecording();
376 | mAudioStart = true;
377 | mAudioReadHandler.sendEmptyMessage(AUDIO_READ);
378 | } catch (Exception e) {
379 | e.printStackTrace();
380 | mAudioStart = false;
381 | }
382 |
383 | break;
384 | case AUDIO_READ:
385 | if (mAudioStart) {
386 | byte[] data = new byte[2560];
387 | int len = 0;
388 | try {
389 | len = mAudioRecord.read(data, 0, data.length);
390 | if (!testAduio) {
391 | byte[] carLifeMsg = exportVideoMsg(MSG_MEDIA_DATA, data, len);
392 | byte[] headmsg = new byte[8];
393 | headmsg[3] = MEDIA;
394 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
395 | CarMsg carMsg = new CarMsg(headmsg, carLifeMsg);
396 | mUsbWriteHandler.obtainMessage(MSG_WRITE_AUDIO, carMsg).sendToTarget();
397 | }
398 |
399 | mAudioReadHandler.sendEmptyMessage(AUDIO_READ);
400 |
401 | } catch (Exception e) {
402 | e.printStackTrace();
403 | mAudioStart = false;
404 | }
405 |
406 | if (testAduio) {
407 | try {
408 | fileOutputStream.write(data, 0, len);
409 | } catch (IOException e) {
410 | e.printStackTrace();
411 | }
412 | }
413 | }
414 |
415 | break;
416 | case AUDIO_STOP:
417 | try {
418 | if (mAudioStart) {
419 | mAudioRecord.stop();
420 | mAudioStart = false;
421 | }
422 |
423 | } catch (Exception e) {
424 | e.printStackTrace();
425 | mAudioStart = false;
426 | }
427 | break;
428 | }
429 |
430 | }
431 | }
432 |
433 | private void startUsbTransferThread() {
434 | HandlerThread inthread = new HandlerThread("read");
435 | inthread.start();
436 | mUsbReadHandler = new Handler(inthread.getLooper()) {
437 | @Override
438 | public void handleMessage(@NonNull Message msg) {
439 | super.handleMessage(msg);
440 | switch (msg.what) {
441 | case 0: {
442 | // mMainHandler.postDelayed(runnable_toast,300000 - 3000);
443 | // mMainHandler.postDelayed(runnable,300000);
444 | while (usbOk) {
445 | try {
446 | byte[] data = new byte[8];
447 | int len = mInputStream.read(data);
448 |
449 | if (len == 8) {
450 | int msg_type = data[3];
451 | log("msg_type = " + msg_type + ", read data = " + Arrays.toString(data));
452 | int msgLen = bytesToInt2(data, 4);
453 | log("msgLen = " + msgLen);
454 | byte[] msgdata = new byte[msgLen];
455 | len = mInputStream.read(msgdata);
456 | log("read data = " + Arrays.toString(msgdata));
457 | log("read msg data = " + len + " msgLen " + msgLen);
458 | short carmsgLen = bytesToShort2(msgdata, 0);
459 | int type = bytesToInt2(msgdata, 4);
460 | log("read carmsgLen data = " + carmsgLen + " type " + type);
461 | byte[] carmsg = new byte[carmsgLen];
462 | System.arraycopy(msgdata, 8, carmsg, 0, carmsgLen);
463 | msgdata = carmsg;
464 | if (msg_type == CMD) {
465 | switch (type) {
466 | case MSG_CMD_HU_PROTOCOL_VERSION: {
467 | CarlifeProtocolVersionMatchStatusProto.CarlifeProtocolVersionMatchStatus.Builder builder = CarlifeProtocolVersionMatchStatusProto.CarlifeProtocolVersionMatchStatus.newBuilder();
468 | builder.setMatchStatus(1);
469 | byte[] result = builder.build().toByteArray();
470 | log(" match = " + Arrays.toString(result));
471 | mUsbWriteHandler.obtainMessage(MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS, exportCMDMsg(MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS, result)).sendToTarget();
472 | }
473 | break;
474 | case MSG_CMD_HU_INFO: {
475 | try {
476 | final CarlifeDeviceInfoProto.CarlifeDeviceInfo deviceInfo = CarlifeDeviceInfoProto.CarlifeDeviceInfo.parseFrom(msgdata);
477 | log("os =" + deviceInfo.getOs() + ", cid =" + deviceInfo.getCid() + ", serial =" + deviceInfo.getSerial());
478 |
479 |
480 | } catch (InvalidProtocolBufferException e) {
481 | e.printStackTrace();
482 | }
483 |
484 | CarlifeDeviceInfoProto.CarlifeDeviceInfo.Builder builder = CarlifeDeviceInfoProto.CarlifeDeviceInfo.newBuilder();
485 | builder.setSdkInt(29);
486 | builder.setSdk("29");
487 | builder.setSerial("unknown");
488 | builder.setCid("QKQ1.190828.002");
489 | builder.setBoard("sdm845");
490 | builder.setOs("Android");
491 | builder.setRelease("10");
492 | builder.setHost("c4-miui-ota-bd47.bj");
493 | mUsbWriteHandler.obtainMessage(MSG_CMD_MD_INFO, exportCMDMsg(MSG_CMD_MD_INFO, builder.build().toByteArray())).sendToTarget();
494 | }
495 | break;
496 | case MSG_CMD_VIDEO_ENCODER_INIT: {
497 | try {
498 | CarlifeVideoEncoderInfoProto.CarlifeVideoEncoderInfo encoderInfo = CarlifeVideoEncoderInfoProto.CarlifeVideoEncoderInfo.parseFrom(msgdata);
499 | log("encoderInfo = " + encoderInfo.getWidth() + ", " + encoderInfo.getHeight() + ", " + encoderInfo.getFrameRate());
500 | if (encoderInfo.getWidth() > 10 && encoderInfo.getHeight() > 10) {
501 | mVISWidth = encoderInfo.getWidth();
502 | mVISHeight = encoderInfo.getHeight();
503 | refreshSize();
504 | log("get cheji MirrorWidth = " + mVISWidth + ", MirrorHeight" + mVISHeight);
505 | }
506 |
507 | } catch (InvalidProtocolBufferException e) {
508 | e.printStackTrace();
509 | }
510 |
511 | CarlifeVideoEncoderInfoProto.CarlifeVideoEncoderInfo.Builder builder = CarlifeVideoEncoderInfoProto.CarlifeVideoEncoderInfo.newBuilder();
512 | builder.setFrameRate(mVideoBit);
513 | builder.setWidth((int) mVISWidth);
514 | builder.setHeight((int) mVISHeight);
515 | mUsbWriteHandler.obtainMessage(MSG_CMD_VIDEO_ENCODER_INIT_DONE, exportCMDMsg(MSG_CMD_VIDEO_ENCODER_INIT_DONE, msgdata)).sendToTarget();
516 |
517 |
518 | }
519 | break;
520 | case MSG_CMD_VIDEO_ENCODER_START: {
521 | mUsbWriteHandler.obtainMessage(MSG_CMD_VIDEO_ENCODER_START).sendToTarget();
522 | }
523 | break;
524 | case MSG_CMD_STATISTIC_INFO: {
525 |
526 | try {
527 | final CarlifeStatisticsInfoProto.CarlifeStatisticsInfo statisticsInfo = CarlifeStatisticsInfoProto.CarlifeStatisticsInfo.parseFrom(msgdata);
528 | log("getCuid = " + statisticsInfo.getCuid() + "" + statisticsInfo.getVersionName() + statisticsInfo.getConnectTime() + statisticsInfo.getCrashLog());
529 | mMainHandler.post(new Runnable() {
530 | @Override
531 | public void run() {
532 | mInfoListener.onVISID(statisticsInfo.getCuid());
533 | }
534 | });
535 | } catch (InvalidProtocolBufferException e) {
536 | e.printStackTrace();
537 | }
538 | CarlifeAuthenResultProto.CarlifeAuthenResult.Builder builder = CarlifeAuthenResultProto.CarlifeAuthenResult.newBuilder();
539 | builder.setResult(true);
540 | mUsbWriteHandler.obtainMessage(MSG_CMD_MD_AUTHEN_RESULT, exportCMDMsg(MSG_CMD_MD_AUTHEN_RESULT, builder.build().toByteArray())).sendToTarget();
541 | }
542 | break;
543 |
544 |
545 | }
546 | } else if (msg_type == TOUCH) {
547 | log("read TOUCH data = " + Arrays.toString(msgdata));
548 | switch (type) {
549 | case MSG_TOUCH_CAR_HARD_KEY_CODE: {
550 | CarlifeCarHardKeyCodeProto.CarlifeCarHardKeyCode keyCode = CarlifeCarHardKeyCodeProto.CarlifeCarHardKeyCode.parseFrom(msgdata);
551 | log("keycode = " + keyCode.getKeycode());
552 | switch (keyCode.getKeycode()) {
553 | case KEYCODE_SEEK_SUB: {
554 | previosSong();
555 | }
556 | break;
557 | case KEYCODE_SEEK_ADD: {
558 | nextSong();
559 | }
560 | break;
561 | }
562 |
563 | }
564 | break;
565 | case MSG_TOUCH_ACTION: {
566 | try {
567 | CarlifeTouchActionProto.CarlifeTouchAction action = CarlifeTouchActionProto.CarlifeTouchAction.parseFrom(msgdata);
568 | genarateGesture(action.getAction(), action.getX(), action.getY());
569 | log("encoderInfo = " + action.getX() + ", " + action.getY() + ", " + action.getAction());
570 | } catch (Exception e) {
571 | e.printStackTrace();
572 | }
573 | }
574 | break;
575 | }
576 |
577 |
578 | }
579 |
580 |
581 | } else {
582 | log("read data = " + len + " " + data.length);
583 | }
584 |
585 | } catch (Exception e) {
586 | e.printStackTrace();
587 |
588 | resetUsb();
589 | break;
590 | }
591 |
592 | //SystemClock.sleep(10);
593 | }
594 | }
595 | }
596 | }
597 | };
598 |
599 | HandlerThread outthread = new HandlerThread("write");
600 | outthread.start();
601 | mUsbWriteHandler = new Handler(outthread.getLooper()) {
602 | @Override
603 | public void handleMessage(@NonNull Message msg) {
604 | super.handleMessage(msg);
605 |
606 | try {
607 | switch (msg.what) {
608 | case MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS:
609 | case MSG_CMD_MD_INFO:
610 | case MSG_CMD_MD_AUTHEN_RESULT: {
611 | byte[] carLifeMsg = (byte[]) msg.obj;
612 | byte[] headmsg = new byte[8];
613 | headmsg[3] = CMD;
614 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
615 | mOutputStream.write(headmsg);
616 | log("msg=" + msg.what + "write data =" + Arrays.toString(headmsg));
617 | mOutputStream.write(carLifeMsg);
618 | log("msg=" + msg.what + "write data =" + Arrays.toString(carLifeMsg));
619 | log("write data ok");
620 | }
621 | break;
622 | case MSG_CMD_VIDEO_ENCODER_INIT_DONE: {
623 |
624 | {
625 | byte[] carLifeMsg = (byte[]) msg.obj;
626 | byte[] headmsg = new byte[8];
627 | headmsg[3] = CMD;
628 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
629 | mOutputStream.write(headmsg);
630 | log("msg=" + msg.what + "write data =" + Arrays.toString(headmsg));
631 | mOutputStream.write(carLifeMsg);
632 | log("msg=" + msg.what + "write data =" + Arrays.toString(carLifeMsg));
633 | log("write data ok");
634 | }
635 |
636 | {
637 | byte[] carLifeMsg = exportCMDMsg(MSG_CMD_FOREGROUND, null);
638 | byte[] headmsg = new byte[8];
639 | headmsg[3] = CMD;
640 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
641 | mOutputStream.write(headmsg);
642 | log("msg=" + MSG_CMD_FOREGROUND + "write data =" + Arrays.toString(headmsg));
643 | mOutputStream.write(carLifeMsg);
644 | log("msg=" + MSG_CMD_FOREGROUND + "write data =" + Arrays.toString(carLifeMsg));
645 | log("write data ok");
646 | }
647 |
648 |
649 | }
650 | break;
651 | case MSG_CMD_VIDEO_ENCODER_START: {
652 | log("now start MSG_CMD_VIDEO_ENCODER_START");
653 |
654 | CarlifeMusicInitProto.CarlifeMusicInit.Builder builder = CarlifeMusicInitProto.CarlifeMusicInit.newBuilder();
655 | builder.setSampleRate(48000);
656 | builder.setChannelConfig(2);
657 | builder.setSampleFormat(16);
658 | byte[] carLifeMsg = exportVideoMsg(MSG_MEDIA_INIT, builder.build().toByteArray());
659 | byte[] headmsg = new byte[8];
660 | headmsg[3] = MEDIA;
661 | intToBytes2(carLifeMsg.length, headmsg, 4);//carlifemsg len
662 | mOutputStream.write(headmsg);
663 | log("msg=MSG_MEDIA_INIT" + "write data =" + Arrays.toString(headmsg));
664 | mOutputStream.write(carLifeMsg);
665 | log("msg=MSG_MEDIA_INIT" + "write data =" + Arrays.toString(carLifeMsg));
666 | log("write data ok audiohandler start");
667 |
668 | //mAudioReadHandler.sendEmptyMessage(AudioHandler.AUDIO_START);
669 | mMediaCodecTool.startProjection(mContext, videoDataEncodeListener, REQUEST_CODE, mVISWidth, mVISHeight, mVideoBit, mVideoFrame);
670 | }
671 | break;
672 | case MSG_WRITE_AUDIO:
673 | case MSG_WRITE_VIDEO: {
674 | //log("write audio or video ..................." + msg.what);
675 | CarMsg carMsg = (CarMsg) msg.obj;
676 | mOutputStream.write(carMsg.head);
677 | mOutputStream.write(carMsg.msg);
678 | }
679 | break;
680 | }
681 | } catch (Exception e) {
682 | e.printStackTrace();
683 | resetUsb();
684 | }
685 | }
686 | };
687 | }
688 |
689 | public boolean isSystemApp(Context context) {
690 | return ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) != 0);
691 | }
692 |
693 | public boolean isSystemUpdateApp(Context context) {
694 | return ((context.getApplicationInfo().flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
695 | }
696 |
697 | public interface InfoListener {
698 | void onVISSize(int x, int y);
699 |
700 | void onVISID(String id);
701 | }
702 |
703 | static class CarMsg {
704 | byte[] head;
705 | byte[] msg;
706 |
707 | CarMsg(byte[] b1, byte[] b3) {
708 | head = b1;
709 | msg = b3;
710 | }
711 | }
712 | }
713 |
--------------------------------------------------------------------------------
/app/src/main/java/com/projection/car/Utils.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.DataOutputStream;
7 | import java.io.File;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.InputStreamReader;
11 | import java.util.ArrayList;
12 |
13 | public class Utils {
14 |
15 | public final static String TAG = "CarProjection";
16 |
17 | public static final int REQUEST_CODE = 100;
18 |
19 | public static final byte CMD = 1;
20 | public static final byte VIDEO = 2;
21 | public static final byte MEDIA = 3;
22 | public static final byte TOUCH = 6;
23 |
24 | public static final int ACTION_DOWN = 0;
25 | public static final int ACTION_UP = 1;
26 | public static final int ACTION_MOVE = 2;
27 |
28 |
29 | public static final int MSG_WRITE_AUDIO = 10;
30 |
31 | public static final int MSG_WRITE_VIDEO = 11;
32 |
33 | public static final int MSG_CMD_HU_PROTOCOL_VERSION = 0x00018001;//98305
34 | public static final int MSG_CMD_HU_INFO = 0x00018003;//98307
35 | public static final int MSG_CMD_VIDEO_ENCODER_INIT = 0x00018007;//98311
36 | public static final int MSG_CMD_VIDEO_ENCODER_START = 0x00018009;//98313
37 | public static final int MSG_CMD_STATISTIC_INFO = 0x00018027;//98343
38 |
39 |
40 | public static final int MSG_CMD_PROTOCOL_VERSION_MATCH_STATUS = 0x00010002;//65538
41 | public static final int MSG_CMD_MD_INFO = 0x00010004;//65540
42 | public static final int MSG_CMD_SCREEN_ON = 0x00010018;//65560
43 | public static final int MSG_CMD_VIDEO_ENCODER_INIT_DONE = 0x00010008;//65544
44 | public static final int MSG_CMD_MD_AUTHEN_RESULT = 0x0001004B;//65611
45 | public static final int MSG_CMD_FOREGROUND = 0x0001001B;//65563
46 |
47 |
48 | public static final int MSG_CMD_MD_FEATURE_CONFIG_REQUEST = 0x00010051;//65617
49 | public static final int MSG_CMD_HU_FEATURE_CONFIG_RESPONSE = 0x00018052;//98386
50 |
51 |
52 | // Media通道相关消息
53 | public static final int MSG_MEDIA_INIT = 0x00030001;
54 | public static final int MSG_MEDIA_STOP = 0x00030002;
55 | public static final int MSG_MEDIA_PAUSE = 0x00030003;
56 | public static final int MSG_MEDIA_RESUME_PLAY = 0x00030004;
57 | public static final int MSG_MEDIA_SEEK_TO = 0x00030005;
58 | public static final int MSG_MEDIA_DATA = 0x00030006;
59 |
60 | public static final int MSG_VIDEO_DATA = 0x00020001;
61 |
62 |
63 | // Touch通道相关消息
64 | public static final int MSG_TOUCH_ACTION = 0x00068001;
65 | public static final int MSG_TOUCH_ACTION_DOWN = 0x00068002;
66 | public static final int MSG_TOUCH_ACTION_UP = 0x00068003;
67 | public static final int MSG_TOUCH_ACTION_MOVE = 0x00068004;
68 | public static final int MSG_TOUCH_SINGLE_CLICK = 0x00068005;
69 | public static final int MSG_TOUCH_DOUBLE_CLICK = 0x00068006;
70 | public static final int MSG_TOUCH_LONG_PRESS = 0x00068007;
71 | public static final int MSG_TOUCH_CAR_HARD_KEY_CODE = 0x00068008;
72 | public static final int MSG_TOUCH_UI_ACTION_SOUND = 0x00060009;
73 | public static final int MSG_TOUCH_ACTION_BEGIN = 0x0006800A;
74 |
75 |
76 | // 硬按键消息
77 | public static final int KEYCODE_HOME = 0x00000001;
78 | public static final int KEYCODE_PHONE_CALL = 0x00000002;
79 | public static final int KEYCODE_PHONE_END = 0x00000003;
80 | public static final int KEYCODE_PHONE_END_MUTE = 0x00000004;
81 | public static final int KEYCODE_HFP = 0x00000005;
82 | public static final int KEYCODE_SELECTOR_NEXT = 0x00000006;
83 | public static final int KEYCODE_SELECTOR_PREVIOUS = 0x00000007;
84 | public static final int KEYCODE_SETTING = 0x00000008;
85 | public static final int KEYCODE_MEDIA = 0x00000009;
86 | public static final int KEYCODE_RADIO = 0x0000000A;
87 | public static final int KEYCODE_NAVI = 0x0000000B;
88 | public static final int KEYCODE_SRC = 0x0000000C;
89 | public static final int KEYCODE_MODE = 0x0000000D;
90 | public static final int KEYCODE_BACK = 0x0000000E;
91 | public static final int KEYCODE_SEEK_SUB = 0x0000000F;
92 | public static final int KEYCODE_SEEK_ADD = 0x00000010;
93 | public static final int KEYCODE_VOLUME_SUB = 0x00000011;
94 | public static final int KEYCODE_VOLUME_ADD = 0x00000012;
95 | public static final int KEYCODE_MUTE = 0x00000013;
96 | public static final int KEYCODE_OK = 0x00000014;
97 | public static final int KEYCODE_MOVE_LEFT = 0x00000015;
98 | public static final int KEYCODE_MOVE_RIGHT = 0x00000016;
99 | public static final int KEYCODE_MOVE_UP = 0x00000017;
100 | public static final int KEYCODE_MOVE_DOWN = 0x00000018;
101 | public static final int KEYCODE_MOVE_UP_LEFT = 0x00000019;
102 | public static final int KEYCODE_MOVE_UP_RIGHT = 0x0000001A;
103 | public static final int KEYCODE_MOVE_DOWN_LEFT = 0x0000001B;
104 | public static final int KEYCODE_MOVE_DOWN_RIGHT = 0x0000001C;
105 | public static final int KEYCODE_TEL = 0x0000001D;
106 | public static final int KEYCODE_MAIN = 0x0000001E;
107 | public static final int KEYCODE_MEDIA_START = 0x0000001F;
108 | public static final int KEYCODE_MEDIA_STOP = 0x00000020;
109 | public static final int KEYCODE_VR_START = 0x00000021;
110 | public static final int KEYCODE_VR_STOP = 0x00000022;
111 | public static final int KEYCODE_NUMBER_0 = 0x00000023;
112 | public static final int KEYCODE_NUMBER_1 = 0x00000024;
113 | public static final int KEYCODE_NUMBER_2 = 0x00000025;
114 | public static final int KEYCODE_NUMBER_3 = 0x00000026;
115 | public static final int KEYCODE_NUMBER_4 = 0x00000027;
116 | public static final int KEYCODE_NUMBER_5 = 0x00000028;
117 | public static final int KEYCODE_NUMBER_6 = 0x00000029;
118 | public static final int KEYCODE_NUMBER_7 = 0x0000002A;
119 | public static final int KEYCODE_NUMBER_8 = 0x0000002B;
120 | public static final int KEYCODE_NUMBER_9 = 0x0000002C;
121 | public static final int KEYCODE_NUMBER_STAR = 0x0000002D; // *
122 | public static final int KEYCODE_NUMBER_POUND = 0x0000002E; // #
123 | public static final int KEYCODE_NUMBER_DEL = 0x0000002F;
124 | public static final int KEYCODE_NUMBER_CLEAR = 0x00000030;
125 | public static final int KEYCODE_NUMBER_ADD = 0x00000031; // +
126 |
127 | public static byte[] exportCMDMsg(int service, byte[] result) {
128 | byte[] carlife = null;
129 | if (result == null) {
130 | carlife = new byte[8];
131 | shortToBytes((short) 0, carlife, 0);// data len
132 | intToBytes2(service, carlife, 4);
133 | } else {
134 | carlife = new byte[8 + result.length];
135 | shortToBytes((short) result.length, carlife, 0);// data len
136 | intToBytes2(service, carlife, 4);
137 | System.arraycopy(result, 0, carlife, 8, result.length);
138 | }
139 |
140 | return carlife;
141 | }
142 |
143 |
144 | public static byte[] exportVideoMsg(int service, byte[] result, int len) {
145 | byte[] carlife = null;
146 | carlife = new byte[12 + len];
147 | intToBytes2(len, carlife, 0);// data len
148 | //Log.e(TAG, "time = " + (int) (System.currentTimeMillis()));
149 | intToBytes2((int) (System.currentTimeMillis()), carlife, 4);
150 | intToBytes2(service, carlife, 8);
151 | System.arraycopy(result, 0, carlife, 12, len);
152 |
153 | return carlife;
154 | }
155 |
156 | public static byte[] exportVideoMsg(int service, byte[] result) {
157 | byte[] carlife = null;
158 | carlife = new byte[12 + result.length];
159 | intToBytes2(result.length, carlife, 0);// data len
160 | //Log.e(TAG, "time = " + (int) (System.currentTimeMillis()));
161 | intToBytes2((int) (System.currentTimeMillis()), carlife, 4);
162 | intToBytes2(service, carlife, 8);
163 | System.arraycopy(result, 0, carlife, 12, result.length);
164 |
165 | return carlife;
166 | }
167 |
168 | public static int getPCMPackageHeadTimeStamp(byte[] mPCMPacakgeHeadBuffer) {
169 | int timeStamp;
170 | int dataHH = (mPCMPacakgeHeadBuffer[4] & 0xff);
171 | int dataHL = (mPCMPacakgeHeadBuffer[5] & 0xff);
172 | int dataLH = (mPCMPacakgeHeadBuffer[6] & 0xff);
173 | int dataLL = (mPCMPacakgeHeadBuffer[7] & 0xff);
174 |
175 | timeStamp =
176 | ((dataHH << 24) & 0xff000000) | ((dataHL << 16) & 0x00ff0000) | ((dataLH << 8) & 0x0000ff00)
177 | | ((dataLL) & 0x000000ff);
178 |
179 | return timeStamp;
180 | }
181 |
182 | public static short bytesToShort2(byte[] src, int offset) {
183 | short value;
184 | value = (short) (((src[offset] & 0xFF) << 8)
185 | | (src[offset + 1] & 0xFF));
186 | return value;
187 | }
188 |
189 | public static int bytesToInt2(byte[] src, int offset) {
190 | int value;
191 | value = (int) (((src[offset] & 0xFF) << 24)
192 | | ((src[offset + 1] & 0xFF) << 16)
193 | | ((src[offset + 2] & 0xFF) << 8)
194 | | (src[offset + 3] & 0xFF));
195 | return value;
196 | }
197 |
198 | // * 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用
199 | public static byte[] intToBytes2(int value) {
200 | byte[] src = new byte[4];
201 | src[0] = (byte) ((value >> 24) & 0xFF);
202 | src[1] = (byte) ((value >> 16) & 0xFF);
203 | src[2] = (byte) ((value >> 8) & 0xFF);
204 | src[3] = (byte) (value & 0xFF);
205 | return src;
206 | }
207 |
208 | public static byte[] intToBytes2(int value, byte[] src, int offset) {
209 |
210 | src[offset] = (byte) ((value >> 24) & 0xFF);
211 | src[offset + 1] = (byte) ((value >> 16) & 0xFF);
212 | src[offset + 2] = (byte) ((value >> 8) & 0xFF);
213 | src[offset + 3] = (byte) (value & 0xFF);
214 | return src;
215 | }
216 |
217 | public static byte[] shortToBytes(short value) {
218 | byte[] src = new byte[2];
219 | src[0] = (byte) ((value >> 8) & 0xFF);
220 | src[1] = (byte) (value & 0xFF);
221 | return src;
222 | }
223 |
224 | public static byte[] shortToBytes(short value, byte[] src, int offset) {
225 | src[offset] = (byte) ((value >> 8) & 0xFF);
226 | src[offset + 1] = (byte) (value & 0xFF);
227 | return src;
228 | }
229 |
230 | public static void logAll() {
231 | new Thread(new Runnable() {
232 |
233 | @Override
234 | public void run() {
235 | android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
236 | Process logcatProcess = null;
237 | BufferedReader bufferedReader = null;
238 | try {
239 | // 获取系统logcat日志信息
240 | String[] running = new String[]{"logcat", "-v",
241 | "time"};
242 | logcatProcess = Runtime.getRuntime().exec(running);
243 | bufferedReader = new BufferedReader(
244 | new InputStreamReader(logcatProcess
245 | .getInputStream()));
246 | StringBuilder logString = new StringBuilder();
247 | String line;
248 | File file = new File("/sdcard/log");
249 | if (file.length() > 1024 * 1024 * 10) {
250 | file.delete();
251 | file = new File("/sdcard/log");
252 | }
253 | FileOutputStream outputStream = new FileOutputStream(file, true);
254 | while ((line = bufferedReader.readLine()) != null) {
255 | logString.append(" ").append(line).append("\n");
256 | // MyLog.logD(TAG, line);
257 | outputStream.write((line + "\n").getBytes());
258 | }
259 | } catch (Exception e) {
260 | e.printStackTrace();
261 | }
262 | }
263 | }).start();
264 | }
265 |
266 | public static boolean reboot(){
267 | try {
268 |
269 | os.writeBytes("reboot\n");
270 | os.flush();
271 | } catch (IOException e) {
272 | e.printStackTrace();
273 | return false;
274 | }
275 |
276 | return true;
277 | }
278 |
279 | public static void root() {
280 | try {
281 | Process process = Runtime.getRuntime().exec("su");
282 | DataOutputStream os = new DataOutputStream(process.getOutputStream());
283 | os.writeBytes("busybox mount -o remount,rw /system\n");
284 | os.writeBytes("cat /sdcard/log > /system/priv-app/log\n");
285 | os.writeBytes("busybox mount -o remount,ro /system\n");
286 | os.writeBytes("exit\n");
287 | os.flush();
288 | } catch (IOException e) {
289 | e.printStackTrace();
290 | }
291 | }
292 |
293 | public static boolean installPlugin(String str) {
294 | try {
295 | // Process process = Runtime.getRuntime().exec("su");
296 | // DataOutputStream os = new DataOutputStream(process.getOutputStream());
297 | os.writeBytes("cat -r " + str + "> /system/priv-app/Projection/projection.apk\n");
298 | os.writeBytes("busybox mount -o remount,rw /system\n");
299 | os.writeBytes("rm -r /system/priv-app/projection.apk\n");
300 | os.writeBytes("cat -r " + str + "> /system/priv-app/projection.apk\n");
301 | os.writeBytes("busybox mount -o remount,ro /system\n");
302 | os.writeBytes("rm " + str + "\n");
303 | // os.writeBytes("exit\n");
304 | os.flush();
305 | } catch (IOException e) {
306 | e.printStackTrace();
307 | return false;
308 | }
309 |
310 | return true;
311 | }
312 |
313 | public static boolean deletPlugin() {
314 | try {
315 | os.writeBytes("rm -r /system/priv-app/projection.apk\n");
316 | os.flush();
317 | } catch (IOException e) {
318 | e.printStackTrace();
319 | return false;
320 | }
321 |
322 | return true;
323 | }
324 |
325 | public static void nextSong() {
326 | try {
327 | // Process process = Runtime.getRuntime().exec("su");
328 | // DataOutputStream os = new DataOutputStream(process.getOutputStream());
329 | os.writeBytes("input keyevent 87\n");
330 | // os.writeBytes("exit\n");
331 | os.flush();
332 | } catch (IOException e) {
333 | e.printStackTrace();
334 | }
335 | }
336 |
337 | public static void pauseSong() {
338 | try {
339 | // Process process = Runtime.getRuntime().exec("su");
340 | // DataOutputStream os = new DataOutputStream(process.getOutputStream());
341 | os.writeBytes("input keyevent 85");
342 | // os.writeBytes("exit\n");
343 | os.flush();
344 | } catch (IOException e) {
345 | e.printStackTrace();
346 | }
347 | }
348 |
349 | public static void stopSong() {
350 | try {
351 | // Process process = Runtime.getRuntime().exec("su");
352 | // DataOutputStream os = new DataOutputStream(process.getOutputStream());
353 | os.writeBytes("input keyevent 86");
354 | // os.writeBytes("exit\n");
355 | os.flush();
356 | } catch (IOException e) {
357 | e.printStackTrace();
358 | }
359 | }
360 |
361 | public static void previosSong() {
362 | try {
363 | // Process process = Runtime.getRuntime().exec("su");
364 | // DataOutputStream os = new DataOutputStream(process.getOutputStream());
365 | os.writeBytes("input keyevent 88 \n");
366 | // os.writeBytes("exit\n");
367 | os.flush();
368 | } catch (IOException e) {
369 | e.printStackTrace();
370 | }
371 | }
372 |
373 | private static DataOutputStream os;
374 |
375 | public static void touch(ArrayList list, long time) {
376 | try {
377 | String command = "\n";
378 | if (list.size() == 2) {
379 | command = "input touchscreen tap " + list.get(0) + " " + list.get(1) + "\n";
380 | } else {
381 | command = "input touchscreen swipe " + list.get(0) + " " + list.get(1) + " " +
382 | list.get(list.size() - 2) + " " + list.get(list.size() - 1) + " " + time + "\n";
383 | }
384 |
385 |
386 | if (os == null) {
387 | Process process = Runtime.getRuntime().exec("su");
388 | os = new DataOutputStream(process.getOutputStream());
389 |
390 | }
391 | os.writeBytes(command);
392 | os.flush();
393 | } catch (IOException e) {
394 | e.printStackTrace();
395 | }
396 | }
397 |
398 |
399 | public static synchronized boolean getRootAhth() {
400 | try {
401 | if (os == null) {
402 | Process process = Runtime.getRuntime().exec("su");
403 | os = new DataOutputStream(process.getOutputStream());
404 |
405 | }
406 | } catch (IOException e) {
407 | e.printStackTrace();
408 | return false;
409 | }
410 |
411 | return true;
412 | }
413 |
414 | public static void log(String str) {
415 | Log.e(TAG, str);
416 | }
417 |
418 | }
419 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeAccelerationProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeAcceleration
3 | {
4 | required double accX = 1;
5 | required double accY = 2;
6 | required double accZ = 3;
7 | optional uint64 timeStamp = 4;
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeAuthenRequestProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeAuthenRequest
3 | {
4 | required string randomValue = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeAuthenResponseProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeAuthenResponse
3 | {
4 | required string encryptValue = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeAuthenResultProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeAuthenResultProto";
4 | message CarlifeAuthenResult
5 | {
6 | required bool result = 1;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpConnectionProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpConnection
3 | {
4 | required int32 state = 1;
5 | optional string address = 2;
6 | optional string name = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpIndicationProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpIndication
3 | {
4 | required int32 state = 1;
5 | optional string phoneNum = 2;
6 | optional string phoneName = 3;
7 | optional string address=4;
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpRequestProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpRequest
3 | {
4 | required int32 command = 1;
5 | optional string phoneNum = 2;
6 | optional int32 dtmfCode = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpResponseProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpResponse
3 | {
4 | required int32 status = 1;
5 | required int32 cmd = 2;
6 | optional int32 dtmfCode = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpStatusRequestProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpStatusRequest
3 | {
4 | required int32 type = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTHfpStatusResponseProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTHfpStatusResponse
3 | {
4 | required int32 status = 1;
5 | required int32 type = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTIdentifyResultIndProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTIdentifyResultInd
3 | {
4 | required int32 status = 1;
5 | required string address = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTPairInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTPairInfo
3 | {
4 | required string address = 1;
5 | optional string passKey = 2;
6 | optional string hash = 3;
7 | optional string randomizer = 4;
8 | required string uuid = 5;
9 | required string name = 6;
10 | required int32 status = 7;
11 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTStartIdentifyReqProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTStartIdentifyReq
3 | {
4 | required string address = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeBTStartPairReqProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeBTStartPairReq
3 | {
4 | required int32 ostype = 1;
5 | optional string address = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeCallRecordsListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeCallRecordsProto.proto";
3 | message CarlifeCallRecordsList
4 | {
5 | required int32 cnt = 1;
6 | repeated com.baidu.carlife.protobuf.CarlifeCallRecords carlifeCallRecords = 2;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeCallRecordsProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeCallRecords
3 | {
4 | required int32 cid = 1;
5 | required string name = 2;
6 | required string number = 3;
7 | required string duration = 4;
8 | required string time = 5;
9 |
10 | enum CallRecordsType
11 | {
12 | DEFAULT_TYPE = 0;
13 | INCOMING_TYPE = 1;
14 | OUTGOING_TYPE = 2;
15 | MISSED_TYPE = 3;
16 | }
17 |
18 | required CallRecordsType type = 6 [default = DEFAULT_TYPE];
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeCarGpsProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeCarGps
3 | {
4 | required uint32 antennaState = 1;
5 | required uint32 signalQuality = 2;
6 | required int32 latitude = 3;
7 | required int32 longitude = 4;
8 | required int32 height = 5;
9 | required uint32 speed = 6;
10 | required uint32 heading = 7;
11 | required uint32 year = 8;
12 | required uint32 month = 9;
13 | required uint32 day = 10;
14 | required uint32 hrs = 11;
15 | required uint32 min = 12;
16 | required uint32 sec = 13;
17 | required uint32 fix = 14;
18 | required uint32 hdop = 15;
19 | required uint32 pdop = 16;
20 | required uint32 vdop = 17;
21 | required uint32 satsUsed = 18;
22 | required uint32 satsVisible = 19;
23 | required uint32 horPosError = 20;
24 | required uint32 vertPosError = 21;
25 | required int32 northSpeed = 22;
26 | required int32 eastSpeed = 23;
27 | required int32 vertSpeed = 24;
28 | optional uint64 timeStamp = 25;
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeCarHardKeyCodeProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeCarHardKeyCode
3 | {
4 | required int32 keycode = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeCarSpeedProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeCarSpeed
3 | {
4 | required int32 speed = 1;
5 | optional uint64 timeStamp = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeConnectExceptionProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeConnectException
3 | {
4 | required int32 exceptionType = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeContactsListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeContactsProto.proto";
3 | message CarlifeContactsList
4 | {
5 | required int32 cnt = 1;
6 | repeated com.baidu.carlife.protobuf.CarlifeContacts carlifeContacts = 2;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeContactsProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeContacts
3 | {
4 | required int32 cid = 1;
5 | required string name = 2;
6 | required string number = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeDeviceInfoProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeDeviceInfoProto";
4 | message CarlifeDeviceInfo
5 | {
6 | optional string os = 1;
7 | optional string board = 2;
8 | optional string bootloader = 3;
9 | optional string brand = 4;
10 | optional string cpu_abi = 5;
11 | optional string cpu_abi2 = 6;
12 | optional string device =7;
13 | optional string display = 8;
14 | optional string fingerprint = 9;
15 | optional string hardware = 10;
16 | optional string host = 11;
17 | optional string cid = 12;
18 | optional string manufacturer = 13;
19 | optional string model = 14;
20 | optional string product = 15;
21 | optional string serial = 16;
22 | optional string codename = 17;
23 | optional string incremental = 18;
24 | optional string release = 19;
25 | optional string sdk = 20;
26 | optional int32 sdk_int = 21;
27 | optional string token = 22;
28 | optional string btaddress = 23;
29 | optional string btname = 24;
30 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeErrorCodeProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeErrorCode
3 | {
4 | required string errorCode = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeFeatureConfigListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeFeatureConfigProto.proto";
3 | message CarlifeFeatureConfigList
4 | {
5 | required int32 cnt = 1;
6 | repeated com.baidu.carlife.protobuf.CarlifeFeatureConfig featureConfig = 2;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeFeatureConfigProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeFeatureConfig
3 | {
4 | required string key = 1;
5 | required int32 value = 2;
6 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeGearInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeGearInfo
3 | {
4 | required int32 gear = 1;
5 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeGyroscopeProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeGyroscope
3 | {
4 | required int32 gyroType = 1;
5 | required double gyroX = 2;
6 | required double gyroy = 3;
7 | required double gyroZ = 4;
8 | optional uint64 timeStamp = 5;
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeMediaInfoListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeMediaInfoProto.proto";
3 | message CarlifeMediaInfoList
4 | {
5 | required int32 cnt = 1;
6 | repeated com.baidu.carlife.protobuf.CarlifeMediaInfo mediaInfo = 2;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeMediaInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeMediaInfo
3 | {
4 | required string source = 1;
5 | required string song = 2;
6 | required string artist = 3;
7 | required string album = 4;
8 | required bytes albumArt = 5;
9 | required int32 duration = 6;
10 | required int32 playlistNum = 7;
11 | required string songId = 8;
12 | required int32 mode = 9;
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeMediaProgressBarProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeMediaProgressBar
3 | {
4 | required int32 progressBar = 1;
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeModuleStatusListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeModuleStatusProto.proto";
3 | message CarlifeModuleStatusList
4 | {
5 | required int32 cnt = 1;
6 | repeated com.baidu.carlife.protobuf.CarlifeModuleStatus moduleStatus = 2;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeModuleStatusProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeModuleStatus
3 | {
4 | required int32 moduleID = 1;
5 | required int32 statusID = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeMusicInitProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeMusicInit
3 | {
4 | required int32 sampleRate = 1;
5 | required int32 channelConfig = 2;
6 | required int32 sampleFormat = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeNaviAssitantGuideInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeNaviAssitantGuideInfo
3 | {
4 | required int32 action = 1;
5 | required int32 assistantType = 2;
6 | required int32 trafficSignType=3;
7 | required int32 totalDistance=4;
8 | required int32 remainDistance=5;
9 | required int32 cameraSpeed=6;
10 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeNaviNextTurnInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeNaviNextTurnInfo
3 | {
4 | required int32 action=1;
5 | required int32 nextTurn=2;
6 | required string roadName=3;
7 | required int32 totalDistance=4;
8 | required int32 remainDistance=5;
9 | required int32 time=6;
10 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeOilProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeOil
3 | {
4 | required int32 level = 1;
5 | required int32 range = 2;
6 | optional bool lowFuleWarning = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeProtocolVersionMatchStatusProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeProtocolVersionMatchStatusProto";
4 | message CarlifeProtocolVersionMatchStatus
5 | {
6 | required int32 matchStatus = 1;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeProtocolVersionProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeProtocolVersionProto";
4 | message CarlifeProtocolVersion
5 | {
6 | required int32 majorVersion = 1;
7 | required int32 minorVersion = 2;
8 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeStatisticsInfoProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeStatisticsInfoProto";
4 | message CarlifeStatisticsInfo
5 | {
6 | required string cuid = 1;
7 | required string versionName = 2;
8 | required int32 versionCode = 3;
9 | required string channel = 4;
10 | required int32 connectCount = 5;
11 | required int32 connectSuccessCount = 6;
12 | required int32 connectTime = 7;
13 | optional string crashLog = 8;
14 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeSubscribeMobileCarLifeInfoListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeSubscribeMobileCarLifeInfoProto.proto";
3 | message CarlifeSubscribeMobileCarLifeInfoList
4 | {
5 | required int32 cnt = 1;
6 | repeated CarlifeSubscribeMobileCarLifeInfo subscribemobileCarLifeInfo = 2;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeSubscribeMobileCarLifeInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeSubscribeMobileCarLifeInfo
3 | {
4 | required int32 moduleID = 1;
5 | optional bool supportFlag = 2;
6 | optional int32 frequency=3;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTTSInitProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTTSInit
3 | {
4 | required int32 sampleRate = 1;
5 | required int32 channelConfig = 2;
6 | required int32 sampleFormat = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchActionProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTouchAction
3 | {
4 | required int32 action = 1;
5 | required int32 x = 2;
6 | required int32 y = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchEventAllDeviceProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeTouchEventDeviceProto.proto";
3 | message CarlifeTouchEventAllDevice
4 | {
5 | required int32 version = 1;
6 | required int32 cnt = 2;
7 | repeated com.baidu.carlife.protobuf.CarlifeTouchEventDevice devices = 3;
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchEventDeviceProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeTouchEventProto.proto";
3 | message CarlifeTouchEventDevice
4 | {
5 | required int32 cid = 1;
6 | required int32 eventx = 2;
7 | required int32 screen_width = 3;
8 | required int32 screen_height = 4;
9 | required int32 abs_x_min = 5;
10 | required int32 abs_x_max = 6;
11 | required int32 abs_y_min = 7;
12 | required int32 abs_y_max = 8;
13 | required string device = 9;
14 | repeated com.baidu.carlife.protobuf.CarlifeTouchEvent downEvent = 10;
15 | repeated com.baidu.carlife.protobuf.CarlifeTouchEvent upEvent = 11;
16 | repeated com.baidu.carlife.protobuf.CarlifeTouchEvent moveEvent = 12;
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchEventProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTouchEvent
3 | {
4 | required int32 type = 1;
5 | required int32 code = 2;
6 | required int32 value = 3;
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchFlingProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTouchFling
3 | {
4 | required int32 x1 = 1;
5 | required int32 y1 = 2;
6 | required int32 x2 = 3;
7 | required int32 y2 = 4;
8 | required float velocityX = 5;
9 | required float velocityY = 6;
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchScrollProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTouchScroll
3 | {
4 | required int32 x1 = 1;
5 | required int32 y1 = 2;
6 | required int32 x2 = 3;
7 | required int32 y2 = 4;
8 | required float distanceX = 5;
9 | required float distanceY = 6;
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeTouchSinglePointProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeTouchSinglePoint
3 | {
4 | required int32 x = 1;
5 | required int32 y = 2;
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeVehicleInfoListProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | import "CarlifeVehicleInfoProto.proto";
3 | message CarlifeVehicleInfoList
4 | {
5 | required int32 cnt = 1;
6 | repeated CarlifeVehicleInfo vehicleInfo = 2;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeVehicleInfoProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeVehicleInfo
3 | {
4 | required int32 moduleID = 1;
5 | optional int32 flag = 2;
6 | optional int32 frequency=3;
7 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeVideoEncoderInfoProto.proto:
--------------------------------------------------------------------------------
1 |
2 | package com.example.car;
3 | option java_outer_classname = "CarlifeVideoEncoderInfoProto";
4 | message CarlifeVideoEncoderInfo
5 | {
6 | required int32 width = 1;
7 | required int32 height = 2;
8 | required int32 frameRate = 3;
9 | }
--------------------------------------------------------------------------------
/app/src/main/proto/CarlifeVideoFrameRateProto.proto:
--------------------------------------------------------------------------------
1 | package com.baidu.carlife.protobuf;
2 | message CarlifeVideoFrameRate
3 | {
4 | required int32 frameRate = 1;
5 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
23 |
24 |
25 |
30 |
31 |
35 |
36 |
41 |
42 |
43 |
44 |
49 |
50 |
55 |
56 |
60 |
61 |
65 |
66 |
70 |
71 |
75 |
76 |
81 |
82 |
83 |
84 |
85 |
90 |
91 |
96 |
97 |
103 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | car
3 | 车机投屏服务,部分功能需要开启服务
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessibility.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessory_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/c.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | maven { url 'https://maven.aliyun.com/repository/central' }
6 | maven { url 'https://maven.aliyun.com/repository/public' }
7 | maven { url 'https://maven.aliyun.com/repository/google' }
8 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
9 | maven { url 'https://maven.aliyun.com/repository/spring' }
10 | maven { url 'https://maven.aliyun.com/repository/spring-plugin' }
11 | maven { url 'https://maven.aliyun.com/repository/grails-core' }
12 | maven { url 'https://maven.aliyun.com/repository/apache-snapshots' }
13 |
14 | }
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:3.3.2'
17 | classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
18 | // NOTE: Do not place your application dependencies here; they belong
19 | // in the individual module build.gradle files
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | maven { url 'https://maven.aliyun.com/repository/central' }
26 | maven { url 'https://maven.aliyun.com/repository/public' }
27 | maven { url 'https://maven.aliyun.com/repository/google' }
28 | maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
29 | maven { url 'https://maven.aliyun.com/repository/spring' }
30 | maven { url 'https://maven.aliyun.com/repository/spring-plugin' }
31 | maven { url 'https://maven.aliyun.com/repository/grails-core' }
32 | maven { url 'https://maven.aliyun.com/repository/apache-snapshots' }
33 |
34 | }
35 | }
36 |
37 | task clean(type: Delete) {
38 | delete rootProject.buildDir
39 | }
40 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/demo.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 |
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Feb 23 15:52:57 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/plugin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "29.0.3"
6 |
7 |
8 | defaultConfig {
9 | applicationId "com.projection.car"
10 | minSdkVersion 15
11 | targetSdkVersion 19
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 |
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 |
31 | implementation "com.android.support:appcompat-v7:26.1.0"
32 | }
33 |
--------------------------------------------------------------------------------
/plugin/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/plugin/src/androidTest/java/com/projection/car/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.projection.car", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/plugin/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/plugin/src/main/java/com/projection/car/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | setContentView(R.layout.activity_main);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/plugin/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/plugin/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/plugin/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/plugin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/plugin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/plugin/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/plugin/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | plugin
3 |
4 |
--------------------------------------------------------------------------------
/plugin/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/plugin/src/test/java/com/projection/car/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.projection.car;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/screen_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aa112901/CarProjection/b1ae81117e4baaf809d594fd11af25a0534051fa/screen_1.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':plugin', ':plugin'
2 |
--------------------------------------------------------------------------------