Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions android-launcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Android launcher skeleton for MonkeyCode repository.

Contains minimal Android Studio project structure with provided launcher skeleton code. Use Android Studio to import the project (open folder android-launcher) and sync Gradle. Build and run on device/emulator with SDK >=29.
30 changes: 30 additions & 0 deletions android-launcher/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
id 'com.android.application'
}

android {
compileSdk 33

defaultConfig {
applicationId "com.x8.launcher"
minSdk 29
targetSdk 30
versionCode 1
versionName "0.1"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.viewpager2:viewpager2:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
}
67 changes: 67 additions & 0 deletions android-launcher/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.x8.launcher">

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

<!-- 领克车控私有权限 -->
<uses-permission android:name="com.lynk.permission.HVAC" />
<uses-permission android:name="com.lynk.permission.VEHICLE_CONTROL" />
<!-- 车载标准控制权限 -->
<uses-permission android:name="android.car.permission.CAR_PROPERTY_ACCESS" />
<uses-permission android:name="android.car.permission.CAR_CONTROL_WINDOWS" />
<!-- 应用安装卸载监听权限 -->
<uses-permission android:name="android.permission.PACKAGE_ADDED" />
<uses-permission android:name="android.permission.PACKAGE_REMOVED" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="小八复刻桌面"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">

<activity
android:name=".MainLauncherActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:windowNoTitle="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<activity android:name=".ui.SettingActivity" android:exported="false"/>

<service
android:name=".floatview.FloatSideBarService"
android:exported="false"/>

<receiver android:name=".receiver.BootCompleteReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>

<receiver android:name=".receiver.AppInstallReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>

</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.x8.launcher.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.x8.launcher.R;
import com.x8.launcher.bean.AppInfo;
import java.util.List;

public class DesktopPageAdapter extends RecyclerView.Adapter<DesktopPageAdapter.PageViewHolder> {
private Context mContext;
private List<AppInfo> desktopAppList;

public DesktopPageAdapter(Context context, List<AppInfo> list) {
this.mContext = context;
this.desktopAppList = list;
}

@NonNull
@Override
public PageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 加载单页桌面图标网格布局
View v = LayoutInflater.from(mContext).inflate(R.layout.item_desktop_page, parent, false);
return new PageViewHolder(v);
}

@Override
public void onBindViewHolder(@NonNull PageViewHolder holder, int position) {
// 简化:目前每页显示全部应用的一个 GridView,实际应按行列分页
GridView gv = holder.itemView.findViewById(R.id.gv_app_grid);
// adapter and drag logic to be implemented in full version
}

@Override
public int getItemCount() {
// 根据应用数量、桌面行列配置计算总页数,简化为 1
return 1;
}

public static class PageViewHolder extends RecyclerView.ViewHolder {
public PageViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.x8.launcher.bean;

import android.graphics.drawable.Drawable;

public class AppInfo {
private String appName;
private String packageName;
private Drawable appIcon;
private String versionName;
private boolean isSystemApp;
private boolean isHideApp;
private boolean isDockApp;

public String getAppName() { return appName; }
public void setAppName(String appName) { this.appName = appName; }
public String getPackageName() { return packageName; }
public void setPackageName(String packageName) { this.packageName = packageName; }
public Drawable getAppIcon() { return appIcon; }
public void setAppIcon(Drawable appIcon) { this.appIcon = appIcon; }
public String getVersionName() { return versionName; }
public void setVersionName(String versionName) { this.versionName = versionName; }
public boolean isSystemApp() { return isSystemApp; }
public void setSystemApp(boolean systemApp) { isSystemApp = systemApp; }
public boolean isHideApp() { return isHideApp; }
public void setHideApp(boolean hideApp) { isHideApp = hideApp; }
public boolean isDockApp() { return isDockApp; }
public void setDockApp(boolean dockApp) { isDockApp = dockApp; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.x8.launcher.bean;

public class CarStatusInfo {
private float speed;
private float fuelLevel;

public float getSpeed() { return speed; }
public void setSpeed(float speed) { this.speed = speed; }
public float getFuelLevel() { return fuelLevel; }
public void setFuelLevel(float fuelLevel) { this.fuelLevel = fuelLevel; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.x8.launcher.car;

import android.content.Context;
import android.content.Intent;

public class VehicleControlManager {
private Context mContext;

public VehicleControlManager(Context context){
this.mContext = context;
}

// 设置空调温度
public void setAcTemp(int temp){
Intent intent = new Intent("com.lynk.action.HVAC_CONTROL");
intent.putExtra("temp_main",temp);
mContext.sendBroadcast(intent);
}

// 控制电动尾门开关
public void openTailGate(boolean open){
Intent intent = new Intent("com.lynk.action.TAILGATE_CONTROL");
intent.putExtra("open", open);
mContext.sendBroadcast(intent);
}

// 切换驾驶模式 经济/舒适/运动
public void changeDriveMode(int mode){
Intent intent = new Intent("com.lynk.action.DRIVE_MODE_CHANGE");
intent.putExtra("mode", mode);
mContext.sendBroadcast(intent);
}

// 唤起360全景影像
public void open360Camera(){
Intent intent = new Intent("com.lynk.action.OPEN_360_CAMERA");
mContext.sendBroadcast(intent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.x8.launcher.car;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.x8.launcher.bean.CarStatusInfo;

public class VehicleDataManager {
private Context mContext;
private CarStatusInfo current;

public VehicleDataManager(Context context){
this.mContext = context;
current = new CarStatusInfo();
registerCarStatusReceiver();
}

// 注册车况广播监听,解析车速、油量、空调、门窗数据
private void registerCarStatusReceiver(){
IntentFilter filter = new IntentFilter();
// 示例私有广播,具体以车厂为准
filter.addAction("com.lynk.action.VEHICLE_STATUS");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 解析车况数据并填充 current(示例字段)
if ("com.lynk.action.VEHICLE_STATUS".equals(intent.getAction())){
current.setSpeed(intent.getFloatExtra("speed", 0f));
current.setFuelLevel(intent.getFloatExtra("fuel", 0f));
}
}
}, filter);
}

// 获取最新整车状态数据
public CarStatusInfo getCurrentCarInfo(){
return current;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.x8.launcher.floatview;

import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import com.x8.launcher.R;

public class FloatBarManager {
private Context mContext;
private WindowManager windowManager;
private View floatSideView;
private WindowManager.LayoutParams layoutParams;

public FloatBarManager(Context context) {
this.mContext = context;
windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}

public void createSideFloatView() {
if (floatSideView != null) return;
floatSideView = LayoutInflater.from(mContext).inflate(R.layout.layout_float_sidebar, null);
initWindowParams();
try {
windowManager.addView(floatSideView, layoutParams);
} catch (Exception e) {
// ignore if cannot add (permission)
}
bindFloatEvent();
}

private void initWindowParams() {
layoutParams = new WindowManager.LayoutParams();
layoutParams.width = (int) (260 * mContext.getResources().getDisplayMetrics().density);
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.format = PixelFormat.TRANSLUCENT;
layoutParams.type = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.gravity = Gravity.START | Gravity.TOP;
layoutParams.x = 0;
layoutParams.y = 0;
}

private void bindFloatEvent() {
floatSideView.setOnTouchListener(new View.OnTouchListener() {
private int lastX, lastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = rawX;
lastY = rawY;
return true;
case MotionEvent.ACTION_MOVE:
int dx = rawX - lastX;
int dy = rawY - lastY;
layoutParams.x += dx;
layoutParams.y += dy;
try { windowManager.updateViewLayout(floatSideView, layoutParams); } catch (Exception ignored) {}
lastX = rawX; lastY = rawY;
return true;
}
return false;
}
});
}

public void destroyFloatView() {
if (floatSideView != null) {
try { windowManager.removeView(floatSideView); } catch (Exception ignored) {}
floatSideView = null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.x8.launcher.floatview;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class FloatSideBarService extends Service {
private FloatBarManager floatBarManager;

@Override
public void onCreate() {
super.onCreate();
floatBarManager = new FloatBarManager(this);
floatBarManager.createSideFloatView();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onDestroy() {
super.onDestroy();
if (floatBarManager != null) {
floatBarManager.destroyFloatView();
}
try { startService(new Intent(this, FloatSideBarService.class)); } catch (Exception ignored) {}
}
}
Loading