diff --git a/gitsion/build.properties b/gitsion/build.properties index de7b289..010c564 100644 --- a/gitsion/build.properties +++ b/gitsion/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Jun 04 18:23:05 HKT 2026 +#Thu Jun 04 19:50:05 HKT 2026 stageCount=27 libraryProject= baseVersion=15.11 publishVersion=15.11.26 -buildCount=47 +buildCount=53 baseBetaVersion=15.11.27 diff --git a/gitsion/src/main/AndroidManifest.xml b/gitsion/src/main/AndroidManifest.xml index 0dc4dde..e55e48c 100644 --- a/gitsion/src/main/AndroidManifest.xml +++ b/gitsion/src/main/AndroidManifest.xml @@ -56,6 +56,7 @@ + diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/App.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/App.java index ca563a9..e015452 100644 --- a/gitsion/src/main/java/cc/winboll/studio/gitsion/App.java +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/App.java @@ -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); diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryActivity.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryActivity.java new file mode 100644 index 0000000..10a83bb --- /dev/null +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryActivity.java @@ -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 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 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 mData; + private final SimpleDateFormat mTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + + GpsHistoryAdapter(List data) { + mData = data; + } + + void setData(List 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; + } + } +} diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryManager.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryManager.java new file mode 100644 index 0000000..071cdac --- /dev/null +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/GpsHistoryManager.java @@ -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 mListeners = new ArrayList(); + 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 getRecords() { + List list = new ArrayList(); + 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)) + ); + } +} diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/MainActivity.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/MainActivity.java index 0a89c1e..9c12e9b 100644 --- a/gitsion/src/main/java/cc/winboll/studio/gitsion/MainActivity.java +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/MainActivity.java @@ -160,6 +160,10 @@ 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; @@ -227,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坐标"); } }); diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/MainService.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/MainService.java index 900d2dd..8a1f655 100644 --- a/gitsion/src/main/java/cc/winboll/studio/gitsion/MainService.java +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/MainService.java @@ -196,6 +196,20 @@ public final class MainService extends Service { 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 subscribeAllMap = mSubscribeManager.getSubscribeMap(); for (Map.Entry entry : subscribeAllMap.entrySet()) { @@ -222,6 +236,7 @@ public final class MainService extends Service { currentTime ); mSubscribeManager.sendSubscribeResult(result); + GpsHistoryManager.getInstance().addSystemRecord(result); LogUtils.d(TAG, "推送GPS数据至订阅者 SID:" + subscribeSid); } } diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/db/GpsHistoryDatabaseHelper.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/db/GpsHistoryDatabaseHelper.java new file mode 100644 index 0000000..a5137b6 --- /dev/null +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/db/GpsHistoryDatabaseHelper.java @@ -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); + } +} diff --git a/gitsion/src/main/java/cc/winboll/studio/gitsion/model/GpsHistoryRecord.java b/gitsion/src/main/java/cc/winboll/studio/gitsion/model/GpsHistoryRecord.java new file mode 100644 index 0000000..18da982 --- /dev/null +++ b/gitsion/src/main/java/cc/winboll/studio/gitsion/model/GpsHistoryRecord.java @@ -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; + } +} diff --git a/gitsion/src/main/res/layout/activity_gps_history.xml b/gitsion/src/main/res/layout/activity_gps_history.xml new file mode 100644 index 0000000..fa7603d --- /dev/null +++ b/gitsion/src/main/res/layout/activity_gps_history.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gitsion/src/main/res/layout/list_item_gps_history.xml b/gitsion/src/main/res/layout/list_item_gps_history.xml new file mode 100644 index 0000000..05de355 --- /dev/null +++ b/gitsion/src/main/res/layout/list_item_gps_history.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/gitsion/src/main/res/menu/menu_main.xml b/gitsion/src/main/res/menu/menu_main.xml index 71c340e..1fbf8b0 100644 --- a/gitsion/src/main/res/menu/menu_main.xml +++ b/gitsion/src/main/res/menu/menu_main.xml @@ -6,6 +6,11 @@ android:title="About" android:icon="@android:drawable/ic_menu_info_details" app:showAsAction="ifRoom"/> +