Compare commits

...

5 Commits

Author SHA1 Message Date
094239edfa OpenCode崩溃现场源码存档 2026-06-04 20:31:51 +08:00
15727b8d66 调试模式下主窗口工具栏添加"应用日志"菜单入口
- menu_main.xml 新增 group_debug 组,内含"应用日志"菜单项(默认隐藏)
- MainActivity.onCreateOptionsMenu 判断 GlobalApplication.isDebugging(),调试状态下显示该组
- MainActivity.onOptionsItemSelected 处理菜单点击,调用 LogActivity.startLogActivity(this, false) 打开日志窗口
2026-06-04 18:26:28 +08:00
c08bbf7b74 移除LogView控件并修复AboutView在Android 16上的Inflate崩溃
- 移除主窗口 activity_main.xml 底部 LogView 面板,清理 MainActivity.java 中相关字段与 onResume 调用,修复 NPE 崩溃
- MyAppTheme 补充 aboutViewBackgroundColor/aboutViewTextColor/aboutViewTitleColor/aboutViewDividerColor 属性定义,修复 AboutActivity 在 Android 16 上的 UnsupportedOperationException 崩溃
2026-06-04 18:03:50 +08:00
b2d2d7ac99 修复GPS订阅数据流断裂,打通MainService→广播→ChildService完整链路
- GpsSubscribeResult 增加 latitude/longitude/locationTime 字段及序列化
- GpsSubscribeReceiverService 重写 onCreate/onDestroy 动态注册广播接收器,onStartCommand 读取 SID 并绑定
- GpsSubscribeControlView 启动服务时传递 EXTRA_SUBSCRIBE_SID
- MainService 步长判断通过后调用 sendSubscribeResult();initManager() 补调 initContext(this)
- GpsReceiverChildService{1,2,3} 补调 super.onStartCommand
- AndroidManifest.xml 修正广播 Action 为 cc.winboll.studio.GPS_SUBSCRIBE_CALLBACK
- GpsSubscribeManager/GpsSubscribeObserverReceiver 广播 Extra 键名改用常量
- GpsSubscribeConst 新增 EXTRA_SUBSCRIBE_SID/EXTRA_SUBSCRIBE_RESULT/EXTRA_LOCATION_POINT
2026-06-04 17:51:13 +08:00
b9bc7dfa18 修复Gitsion项目在Android 16上的布局Inflate崩溃
- 修正 activity_main.xml 中 GpsSubscribeControlView 的包名
  (libgpsrelaysentinel → libgitsion)
- 补充 MyAppTheme 缺失的自定义主题属性,新增 DebugActivityTheme
  解决 view_log.xml 中 ?attr/colorTextBackgound 等引用无法
  在 Android 16 (SDK 36) 上解析导致的 InflateException
- 添加 colors.xml 中对应的主题颜色定义
2026-06-04 17:10:14 +08:00
25 changed files with 753 additions and 37 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Wed Jun 03 12:37:33 GMT 2026
#Thu Jun 04 19:50:05 HKT 2026
stageCount=27
libraryProject=
baseVersion=15.11
publishVersion=15.11.26
buildCount=37
buildCount=53
baseBetaVersion=15.11.27

View File

@@ -56,6 +56,7 @@
<service android:name=".GpsReceiverChildService3"/>
<activity android:name=".AboutActivity"/>
<activity android:name=".GpsHistoryActivity"/>
</application>

View File

@@ -57,6 +57,7 @@ public class App extends GlobalApplication {
ToastUtils.init(this);
WinBoLLActivityManager.init(this);
AESThemeUtil.init(null);
GpsHistoryManager.getInstance().init(this);
} catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);

View File

@@ -0,0 +1,151 @@
package cc.winboll.studio.gitsion;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import cc.winboll.studio.gitsion.db.GpsHistoryDatabaseHelper;
import cc.winboll.studio.gitsion.model.GpsHistoryRecord;
public class GpsHistoryActivity extends Activity {
private ListView mListView;
private TextView mEmptyHint;
private TextView mTvSystemCount;
private TextView mTvSimCount;
private GpsHistoryAdapter mAdapter;
private final Runnable mRefreshRunnable = new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
refreshData();
}
});
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gps_history);
mListView = findViewById(R.id.lv_gps_history);
mEmptyHint = findViewById(R.id.tv_empty_hint);
mTvSystemCount = findViewById(R.id.tv_system_count);
mTvSimCount = findViewById(R.id.tv_sim_count);
List<GpsHistoryRecord> records = GpsHistoryManager.getInstance().getRecords();
mAdapter = new GpsHistoryAdapter(records);
mListView.setAdapter(mAdapter);
updateEmptyHint(records.isEmpty());
updateCounts();
}
@Override
protected void onResume() {
super.onResume();
GpsHistoryManager.getInstance().addListener(mRefreshRunnable);
refreshData();
}
@Override
protected void onPause() {
super.onPause();
GpsHistoryManager.getInstance().removeListener(mRefreshRunnable);
}
private void refreshData() {
List<GpsHistoryRecord> records = GpsHistoryManager.getInstance().getRecords();
mAdapter.setData(records);
mAdapter.notifyDataSetChanged();
updateEmptyHint(records.isEmpty());
updateCounts();
}
private void updateCounts() {
mTvSystemCount.setText(String.valueOf(GpsHistoryManager.getInstance().getSystemCount()));
mTvSimCount.setText(String.valueOf(GpsHistoryManager.getInstance().getSimCount()));
}
private void updateEmptyHint(boolean empty) {
mEmptyHint.setVisibility(empty ? View.VISIBLE : View.GONE);
mListView.setVisibility(empty ? View.GONE : View.VISIBLE);
}
private static class GpsHistoryAdapter extends BaseAdapter {
private List<GpsHistoryRecord> mData;
private final SimpleDateFormat mTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
GpsHistoryAdapter(List<GpsHistoryRecord> data) {
mData = data;
}
void setData(List<GpsHistoryRecord> data) {
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(parent.getContext(), R.layout.list_item_gps_history, null);
}
GpsHistoryRecord item = mData.get(position);
LinearLayout root = (LinearLayout) convertView.findViewById(R.id.layout_record_root);
TextView tvType = (TextView) convertView.findViewById(R.id.tv_record_type);
TextView tvTime = (TextView) convertView.findViewById(R.id.tv_record_time);
TextView tvCoord = (TextView) convertView.findViewById(R.id.tv_record_coord);
String timeStr = mTimeFormat.format(new Date(item.getLocationTime()));
String coordStr = String.format(Locale.getDefault(),
"纬度: %.6f 经度: %.6f SID: %s",
item.getLatitude(), item.getLongitude(), item.getSid());
tvTime.setText(timeStr);
tvCoord.setText(coordStr);
if (item.isSim()) {
root.setBackgroundColor(Color.parseColor("#2a3a2a"));
tvType.setText("[模拟数据]");
tvType.setTextColor(Color.parseColor("#ffb74d"));
} else {
root.setBackgroundColor(Color.parseColor("#1c1c1c"));
tvType.setText("[系统数据]");
tvType.setTextColor(Color.parseColor("#4fc3f7"));
}
return convertView;
}
}
}

View File

@@ -0,0 +1,150 @@
package cc.winboll.studio.gitsion;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import java.util.List;
import cc.winboll.studio.gitsion.db.GpsHistoryDatabaseHelper;
import cc.winboll.studio.gitsion.model.GpsHistoryRecord;
import cc.winboll.studio.libgitsion.model.GpsSubscribeResult;
public final class GpsHistoryManager {
private static final int MAX_RECORDS = 2000;
private static final int TRIM_COUNT = 1000;
private static final GpsHistoryManager sInstance = new GpsHistoryManager();
private final List<Runnable> mListeners = new ArrayList<Runnable>();
private GpsHistoryDatabaseHelper mDbHelper;
private GpsHistoryManager() {}
public static GpsHistoryManager getInstance() {
return sInstance;
}
public void init(Context context) {
mDbHelper = new GpsHistoryDatabaseHelper(context.getApplicationContext());
}
public void addRecord(GpsSubscribeResult result, int type) {
if (mDbHelper == null) return;
SQLiteDatabase db = mDbHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(GpsHistoryDatabaseHelper.COL_LATITUDE, result.getLatitude());
cv.put(GpsHistoryDatabaseHelper.COL_LONGITUDE, result.getLongitude());
cv.put(GpsHistoryDatabaseHelper.COL_LOCATION_TIME, result.getLocationTime());
cv.put(GpsHistoryDatabaseHelper.COL_RECORD_TIME, System.currentTimeMillis());
cv.put(GpsHistoryDatabaseHelper.COL_TYPE, type);
cv.put(GpsHistoryDatabaseHelper.COL_SID, result.getSubscribeUniqueId());
cv.put(GpsHistoryDatabaseHelper.COL_DESC, result.getResultDesc());
db.insert(GpsHistoryDatabaseHelper.TABLE_NAME, null, cv);
trimIfNeeded(db);
notifyListeners();
}
public void addSystemRecord(GpsSubscribeResult result) {
addRecord(result, GpsHistoryDatabaseHelper.TYPE_SYSTEM);
}
public void addSimRecord(GpsSubscribeResult result) {
addRecord(result, GpsHistoryDatabaseHelper.TYPE_SIM);
}
public List<GpsHistoryRecord> getRecords() {
List<GpsHistoryRecord> list = new ArrayList<GpsHistoryRecord>();
if (mDbHelper == null) return list;
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor c = db.query(GpsHistoryDatabaseHelper.TABLE_NAME, null, null, null,
null, null, GpsHistoryDatabaseHelper.COL_ID + " DESC", null);
while (c.moveToNext()) {
list.add(parseCursor(c));
}
c.close();
return list;
}
public int getSystemCount() {
return countByType(GpsHistoryDatabaseHelper.TYPE_SYSTEM);
}
public int getSimCount() {
return countByType(GpsHistoryDatabaseHelper.TYPE_SIM);
}
public void clear() {
if (mDbHelper == null) return;
mDbHelper.getWritableDatabase().delete(GpsHistoryDatabaseHelper.TABLE_NAME, null, null);
notifyListeners();
}
public void addListener(Runnable listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
public void removeListener(Runnable listener) {
synchronized (mListeners) {
mListeners.remove(listener);
}
}
private void notifyListeners() {
synchronized (mListeners) {
for (Runnable r : mListeners) {
r.run();
}
}
}
private int countByType(int type) {
if (mDbHelper == null) return 0;
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor c = db.rawQuery(
"SELECT COUNT(*) FROM " + GpsHistoryDatabaseHelper.TABLE_NAME
+ " WHERE " + GpsHistoryDatabaseHelper.COL_TYPE + "=?",
new String[]{String.valueOf(type)});
int count = 0;
if (c.moveToFirst()) {
count = c.getInt(0);
}
c.close();
return count;
}
private void trimIfNeeded(SQLiteDatabase db) {
Cursor c = db.rawQuery(
"SELECT COUNT(*) FROM " + GpsHistoryDatabaseHelper.TABLE_NAME, null);
int total = 0;
if (c.moveToFirst()) {
total = c.getInt(0);
}
c.close();
if (total > MAX_RECORDS) {
db.execSQL("DELETE FROM " + GpsHistoryDatabaseHelper.TABLE_NAME
+ " WHERE " + GpsHistoryDatabaseHelper.COL_ID + " IN ("
+ " SELECT " + GpsHistoryDatabaseHelper.COL_ID
+ " FROM " + GpsHistoryDatabaseHelper.TABLE_NAME
+ " ORDER BY " + GpsHistoryDatabaseHelper.COL_ID + " ASC"
+ " LIMIT " + TRIM_COUNT + ")");
}
}
private static GpsHistoryRecord parseCursor(Cursor c) {
return new GpsHistoryRecord(
c.getLong(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_ID)),
c.getDouble(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_LATITUDE)),
c.getDouble(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_LONGITUDE)),
c.getLong(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_LOCATION_TIME)),
c.getLong(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_RECORD_TIME)),
c.getInt(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_TYPE)),
c.getString(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_SID)),
c.getString(c.getColumnIndexOrThrow(GpsHistoryDatabaseHelper.COL_DESC))
);
}
}

View File

@@ -21,6 +21,7 @@ public final class GpsReceiverChildService1 extends GpsSubscribeReceiverService
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
}

View File

@@ -20,6 +20,7 @@ public final class GpsReceiverChildService2 extends GpsSubscribeReceiverService
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
}

View File

@@ -20,6 +20,7 @@ public final class GpsReceiverChildService3 extends GpsSubscribeReceiverService
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
}

View File

@@ -19,8 +19,10 @@ import android.view.MenuItem;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import cc.winboll.studio.gitsion.R;
import cc.winboll.studio.libappbase.GlobalApplication;
import cc.winboll.studio.libappbase.LogActivity;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libappbase.LogView;
import cc.winboll.studio.libappbase.ToastUtils;
/**
@@ -33,7 +35,7 @@ public final class MainActivity extends AppCompatActivity {
//原有控件
private Toolbar mToolbar;
private LogView mLogView;
private Switch mSwitchService;
//新增
@@ -81,7 +83,7 @@ public final class MainActivity extends AppCompatActivity {
private void initView() {
//原有
mToolbar = findViewById(R.id.toolbar);
mLogView = findViewById(R.id.logview);
mSwitchService = findViewById(R.id.switch_service);
//新增
@@ -146,6 +148,9 @@ public final class MainActivity extends AppCompatActivity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
if (GlobalApplication.isDebugging()) {
menu.setGroupVisible(R.id.group_debug, true);
}
return true;
}
@@ -155,6 +160,14 @@ public final class MainActivity extends AppCompatActivity {
startActivity(new Intent(this, AboutActivity.class));
return true;
}
if (item.getItemId() == R.id.action_gps_history) {
startActivity(new Intent(this, GpsHistoryActivity.class));
return true;
}
if (item.getItemId() == R.id.action_app_log) {
LogActivity.startLogActivity(this, false);
return true;
}
return super.onOptionsItemSelected(item);
}
@@ -218,6 +231,17 @@ public final class MainActivity extends AppCompatActivity {
@Override
public void onClick(View v) {
saveSimGpsData();
GpsHistoryManager.getInstance().addSimRecord(new cc.winboll.studio.libgitsion.model.GpsSubscribeResult(
"SIM",
cc.winboll.studio.libgitsion.model.GpsSubscribeConst.RESULT_SUCCESS,
"模拟GPS定位",
cc.winboll.studio.libgitsion.model.GpsSubscribeConst.GPS_STATE_LOCATED,
0,
System.currentTimeMillis(),
simLat,
simLng,
System.currentTimeMillis()
));
ToastUtils.show("已设置当前模拟GPS坐标");
}
});
@@ -366,10 +390,6 @@ public final class MainActivity extends AppCompatActivity {
}
}
@Override
protected void onResume() {
super.onResume();
mLogView.start();
}
}

View File

@@ -18,7 +18,9 @@ import androidx.core.app.NotificationCompat;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libgitsion.manager.GpsSubscribeManager;
import cc.winboll.studio.libgitsion.manager.SubscribeLocationManager;
import cc.winboll.studio.libgitsion.model.GpsSubscribeConst;
import cc.winboll.studio.libgitsion.model.GpsSubscribeMsg;
import cc.winboll.studio.libgitsion.model.GpsSubscribeResult;
import java.util.Map;
@@ -85,6 +87,7 @@ public final class MainService extends Service {
*/
private void initManager() {
mSubscribeManager = GpsSubscribeManager.getInstance();
mSubscribeManager.initContext(this);
mLocationRuleManager = SubscribeLocationManager.getInstance();
}
@@ -189,20 +192,52 @@ public final class MainService extends Service {
//更新前台通知文案
updateForegroundNotification(locationInfo);
double currentLat = location.getLatitude();
double currentLng = location.getLongitude();
long currentTime = location.getTime();
//保存每一次系统原始定位数据到历史记录
GpsSubscribeResult rawRecord = new GpsSubscribeResult(
"",
GpsSubscribeConst.RESULT_SUCCESS,
"系统GPS定位",
GpsSubscribeConst.GPS_STATE_LOCATED,
0,
System.currentTimeMillis(),
currentLat,
currentLng,
currentTime
);
GpsHistoryManager.getInstance().addSystemRecord(rawRecord);
//遍历全部订阅者进行推送规则判断
Map<String, GpsSubscribeMsg> subscribeAllMap = mSubscribeManager.getSubscribeMap();
for (Map.Entry<String, GpsSubscribeMsg> entry : subscribeAllMap.entrySet()) {
final String subscribeSid = entry.getKey();
final GpsSubscribeMsg subscribeConfig = entry.getValue();
double currentLat = location.getLatitude();
double currentLng = location.getLongitude();
//判断是否满足推送条件(全订阅/步长阈值)
boolean allowPush = mLocationRuleManager.isNeedPush(subscribeSid, currentLat, currentLng);
if (allowPush) {
//推送成功后刷新该订阅者基准定点坐标
mLocationRuleManager.updateSubscriberPoint(subscribeSid, currentLat, currentLng);
mLocationRuleManager.addPushCount(subscribeSid);
//发送结果广播给订阅者
GpsSubscribeResult result = new GpsSubscribeResult(
subscribeSid,
GpsSubscribeConst.RESULT_SUCCESS,
"GPS定位推送",
GpsSubscribeConst.GPS_STATE_LOCATED,
0,
System.currentTimeMillis(),
currentLat,
currentLng,
currentTime
);
mSubscribeManager.sendSubscribeResult(result);
GpsHistoryManager.getInstance().addSystemRecord(result);
LogUtils.d(TAG, "推送GPS数据至订阅者 SID" + subscribeSid);
}
}
}

View File

@@ -0,0 +1,51 @@
package cc.winboll.studio.gitsion.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public final class GpsHistoryDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "gps_history.db";
private static final int DB_VERSION = 1;
public static final String TABLE_NAME = "gps_history";
public static final String COL_ID = "_id";
public static final String COL_LATITUDE = "latitude";
public static final String COL_LONGITUDE = "longitude";
public static final String COL_LOCATION_TIME = "location_time";
public static final String COL_RECORD_TIME = "record_time";
public static final String COL_TYPE = "type";
public static final String COL_SID = "sid";
public static final String COL_DESC = "description";
public static final int TYPE_SYSTEM = 0;
public static final int TYPE_SIM = 1;
private static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME + " ("
+ COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COL_LATITUDE + " REAL NOT NULL, "
+ COL_LONGITUDE + " REAL NOT NULL, "
+ COL_LOCATION_TIME + " INTEGER NOT NULL, "
+ COL_RECORD_TIME + " INTEGER NOT NULL, "
+ COL_TYPE + " INTEGER NOT NULL DEFAULT " + TYPE_SYSTEM + ", "
+ COL_SID + " TEXT, "
+ COL_DESC + " TEXT"
+ ")";
public GpsHistoryDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}

View File

@@ -0,0 +1,64 @@
package cc.winboll.studio.gitsion.model;
import cc.winboll.studio.gitsion.db.GpsHistoryDatabaseHelper;
public final class GpsHistoryRecord {
private final long id;
private final double latitude;
private final double longitude;
private final long locationTime;
private final long recordTime;
private final int type;
private final String sid;
private final String description;
public GpsHistoryRecord(long id, double latitude, double longitude,
long locationTime, long recordTime,
int type, String sid, String description) {
this.id = id;
this.latitude = latitude;
this.longitude = longitude;
this.locationTime = locationTime;
this.recordTime = recordTime;
this.type = type;
this.sid = sid;
this.description = description;
}
public long getId() {
return id;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public long getLocationTime() {
return locationTime;
}
public long getRecordTime() {
return recordTime;
}
public int getType() {
return type;
}
public boolean isSim() {
return type == GpsHistoryDatabaseHelper.TYPE_SIM;
}
public String getSid() {
return sid;
}
public String getDescription() {
return description;
}
}

View File

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#1c1c1c">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:padding="8dp"
android:background="@drawable/border_gray"
android:layout_margin="4dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="系统GPS"
android:textColor="#aaaaaa"
android:textSize="13sp"/>
<TextView
android:id="@+id/tv_system_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#4fc3f7"
android:textSize="13sp"
android:layout_marginRight="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="模拟GPS"
android:textColor="#aaaaaa"
android:textSize="13sp"/>
<TextView
android:id="@+id/tv_sim_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="#ffb74d"
android:textSize="13sp"/>
</LinearLayout>
<TextView
android:id="@+id/tv_empty_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暂无GPS历史数据"
android:textColor="#666666"
android:gravity="center"
android:padding="24dp"
android:visibility="gone"/>
<ListView
android:id="@+id/lv_gps_history"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -158,19 +158,19 @@
android:gravity="center_horizontal"
android:padding="12dp">
<cc.winboll.studio.libgpsrelaysentinel.view.GpsSubscribeControlView
<cc.winboll.studio.libgitsion.view.GpsSubscribeControlView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/border_gray"/>
<cc.winboll.studio.libgpsrelaysentinel.view.GpsSubscribeControlView
<cc.winboll.studio.libgitsion.view.GpsSubscribeControlView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@drawable/border_gray"/>
<cc.winboll.studio.libgpsrelaysentinel.view.GpsSubscribeControlView
<cc.winboll.studio.libgitsion.view.GpsSubscribeControlView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
@@ -180,19 +180,5 @@
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="vertical"
android:id="@+id/container_log_show"
android:background="@drawable/border_gray">
<cc.winboll.studio.libappbase.LogView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/logview"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_record_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
android:background="@drawable/border_gray"
android:layout_margin="4dp">
<TextView
android:id="@+id/tv_record_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10sp"
android:padding="2dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:layout_marginBottom="2dp"/>
<TextView
android:id="@+id/tv_record_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#aaaaaa"
android:textSize="11sp"/>
<TextView
android:id="@+id/tv_record_coord"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#888888"
android:textSize="12sp"
android:layout_marginTop="2dp"/>
</LinearLayout>

View File

@@ -6,4 +6,15 @@
android:title="About"
android:icon="@android:drawable/ic_menu_info_details"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_gps_history"
android:title="GPS历史"
android:icon="@android:drawable/ic_menu_myplaces"
app:showAsAction="ifRoom"/>
<group android:id="@+id/group_debug">
<item
android:id="@+id/action_app_log"
android:title="应用日志"
android:visible="false"/>
</group>
</menu>

View File

@@ -3,4 +3,12 @@
<color name="colorPrimary">#009688</color>
<color name="colorPrimaryDark">#00796B</color>
<color name="colorAccent">#FF9800</color>
<color name="mainWindowBackgroundColor">#FFF5F5F5</color>
<color name="mainWindowTextColor">#FF000000</color>
<color name="mainWindowDarkBackgroundColor">#FFF5F5F5</color>
<color name="mainWindowDarkTextColor">#FF000000</color>
<color name="toolbarBackgroundColor">#FF009688</color>
<color name="toolbarTextColor">#FF000000</color>
<color name="debugTextColor">#FF808080</color>
</resources>

View File

@@ -2,10 +2,34 @@
<!-- Base application theme. -->
<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="themeDebug">@style/DebugActivityTheme</item>
<item name="mainWindowBackgroundColor">@color/mainWindowBackgroundColor</item>
<item name="mainWindowTextColor">@color/mainWindowTextColor</item>
<item name="mainWindowDarkBackgroundColor">@color/mainWindowDarkBackgroundColor</item>
<item name="mainWindowDarkTextColor">@color/mainWindowDarkTextColor</item>
<item name="toolbarBackgroundColor">@color/toolbarBackgroundColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
<item name="activityBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="activityTextColor">?attr/mainWindowTextColor</item>
<item name="debugTextColor">@color/debugTextColor</item>
<item name="aboutViewBackgroundColor">?attr/mainWindowBackgroundColor</item>
<item name="aboutViewTextColor">?attr/mainWindowTextColor</item>
<item name="aboutViewTitleColor">?attr/mainWindowTextColor</item>
<item name="aboutViewDividerColor">?attr/mainWindowDarkTextColor</item>
</style>
<style name="DebugActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorTittle">?attr/mainWindowTextColor</item>
<item name="colorTittleBackgound">@color/toolbarBackgroundColor</item>
<item name="colorText">?attr/debugTextColor</item>
<item name="colorTextBackgound">?attr/mainWindowBackgroundColor</item>
<item name="debugTextColor">@color/debugTextColor</item>
<item name="toolbarTextColor">@color/toolbarTextColor</item>
</style>
</resources>

View File

@@ -17,7 +17,7 @@
<intent-filter>
<action android:name=".receiver.GpsSubscribeObserverReceiver"/>
<action android:name="cc.winboll.studio.GPS_SUBSCRIBE_CALLBACK"/>
</intent-filter>

View File

@@ -59,7 +59,7 @@ public final class GpsSubscribeManager {
return;
}
Intent intent = new Intent(GpsSubscribeConst.ACTION_SUBSCRIBE_CALLBACK);
intent.putExtra("data",result);
intent.putExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_RESULT, result);
appContext.sendBroadcast(intent);
}

View File

@@ -37,6 +37,11 @@ public final class GpsSubscribeConst {
public static final String ACTION_SUBSCRIBE_REQUEST = "cc.winboll.studio.GPS_SUBSCRIBE_REQUEST";
public static final String ACTION_SUBSCRIBE_CALLBACK = "cc.winboll.studio.GPS_SUBSCRIBE_CALLBACK";
//Intent Extra 键名
public static final String EXTRA_SUBSCRIBE_SID = "extra_subscribe_sid";
public static final String EXTRA_SUBSCRIBE_RESULT = "extra_subscribe_result";
public static final String EXTRA_LOCATION_POINT = "extra_location_point";
//超时毫秒
public static final long SUBSCRIBE_TIME_OUT = 5000;

View File

@@ -17,19 +17,28 @@ public final class GpsSubscribeResult implements Parcelable {
private final int gpsRunningState;
private final long realEffectiveInterval;
private final long currentTimeStamp;
private final double latitude;
private final double longitude;
private final long locationTime;
public GpsSubscribeResult(String subscribeUniqueId,
int resultCode,
String resultDesc,
int gpsRunningState,
long realEffectiveInterval,
long currentTimeStamp) {
long currentTimeStamp,
double latitude,
double longitude,
long locationTime) {
this.subscribeUniqueId = subscribeUniqueId;
this.resultCode = resultCode;
this.resultDesc = resultDesc;
this.gpsRunningState = gpsRunningState;
this.realEffectiveInterval = realEffectiveInterval;
this.currentTimeStamp = currentTimeStamp;
this.latitude = latitude;
this.longitude = longitude;
this.locationTime = locationTime;
}
public String getSubscribeUniqueId() {
@@ -56,6 +65,18 @@ public final class GpsSubscribeResult implements Parcelable {
return currentTimeStamp;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
public long getLocationTime() {
return locationTime;
}
@Override
public int describeContents() {
return 0;
@@ -69,6 +90,9 @@ public final class GpsSubscribeResult implements Parcelable {
dest.writeInt(gpsRunningState);
dest.writeLong(realEffectiveInterval);
dest.writeLong(currentTimeStamp);
dest.writeDouble(latitude);
dest.writeDouble(longitude);
dest.writeLong(locationTime);
}
public static final Creator<GpsSubscribeResult> CREATOR = new Creator<GpsSubscribeResult>() {
@@ -80,6 +104,9 @@ public final class GpsSubscribeResult implements Parcelable {
in.readString(),
in.readInt(),
in.readLong(),
in.readLong(),
in.readDouble(),
in.readDouble(),
in.readLong()
);
}
@@ -98,6 +125,9 @@ public final class GpsSubscribeResult implements Parcelable {
bundle.putInt("gpsState", gpsRunningState);
bundle.putLong("realInterval", realEffectiveInterval);
bundle.putLong("time", currentTimeStamp);
bundle.putDouble("lat", latitude);
bundle.putDouble("lng", longitude);
bundle.putLong("locTime", locationTime);
return bundle;
}
@@ -108,7 +138,10 @@ public final class GpsSubscribeResult implements Parcelable {
bundle.getString("desc"),
bundle.getInt("gpsState"),
bundle.getLong("realInterval"),
bundle.getLong("time")
bundle.getLong("time"),
bundle.getDouble("lat"),
bundle.getDouble("lng"),
bundle.getLong("locTime")
);
}
}

View File

@@ -28,7 +28,7 @@ public final class GpsSubscribeObserverReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(GpsSubscribeConst.ACTION_SUBSCRIBE_CALLBACK.equals(action)){
GpsSubscribeResult result = intent.getParcelableExtra("data");
GpsSubscribeResult result = intent.getParcelableExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_RESULT);
if(listener != null && result != null){
listener.onResultBack(result);
}

View File

@@ -1,11 +1,17 @@
package cc.winboll.studio.libgitsion.service;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.libgitsion.manager.SubscribeLocationManager;
import cc.winboll.studio.libgitsion.model.GpsSubscribeConst;
import cc.winboll.studio.libgitsion.model.GpsSubscribeMsg;
import cc.winboll.studio.libgitsion.model.GpsSubscribeResult;
import cc.winboll.studio.libgitsion.model.LocationPoint;
/**
@@ -19,10 +25,59 @@ public abstract class GpsSubscribeReceiverService extends Service {
//当前绑定的视图订阅SID
protected String bindViewSid;
private BroadcastReceiver mCallbackReceiver;
public void bindControlSid(String sid){
this.bindViewSid = sid;
}
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG_PARENT, "Service onCreate, 注册广播接收器");
mCallbackReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (GpsSubscribeConst.ACTION_SUBSCRIBE_CALLBACK.equals(intent.getAction())) {
GpsSubscribeResult result = intent.getParcelableExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_RESULT);
if (result != null && bindViewSid != null
&& bindViewSid.equals(result.getSubscribeUniqueId())) {
LocationPoint point = new LocationPoint(
result.getLatitude(),
result.getLongitude(),
result.getLocationTime()
);
GpsSubscribeMsg config = SubscribeLocationManager.getInstance()
.getSubscribeConfig(bindViewSid);
LogUtils.d(TAG_PARENT, "收到GPS推送转发至 onReceiveGpsDataSID" + bindViewSid);
onReceiveGpsData(point, config);
}
}
}
};
registerReceiver(mCallbackReceiver, new IntentFilter(GpsSubscribeConst.ACTION_SUBSCRIBE_CALLBACK));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.hasExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_SID)) {
String sid = intent.getStringExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_SID);
bindControlSid(sid);
LogUtils.d(TAG_PARENT, "绑定SID" + sid);
}
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mCallbackReceiver != null) {
unregisterReceiver(mCallbackReceiver);
mCallbackReceiver = null;
LogUtils.d(TAG_PARENT, "广播接收器已注销");
}
}
/**
* 统一接收GPS推送入口
*/

View File

@@ -139,9 +139,10 @@ public final class GpsSubscribeControlView extends LinearLayout {
mLocationManager.putSubscribeConfig(currentSubscribeSid, subscribeMsg);
mLocationManager.clearPushCount(currentSubscribeSid);
//开启订阅自动启动专属接收服务
//开启订阅自动启动专属接收服务携带SID
if(mBindReceiverServiceClazz != null){
Intent startServiceIntent = new Intent(getContext(), mBindReceiverServiceClazz);
startServiceIntent.putExtra(GpsSubscribeConst.EXTRA_SUBSCRIBE_SID, currentSubscribeSid);
getContext().startService(startServiceIntent);
}
}