Этот коммит содержится в:
Anton Tananaev
2021-07-17 19:11:29 -07:00
родитель 958eb77c51
Коммит 02295f5fcf
27 изменённых файлов: 1174 добавлений и 1534 удалений
+34 -42
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2017 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,57 +13,49 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.app.Activity;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.app.Activity
import android.content.IntentFilter
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.preference.PreferenceManager
import com.google.android.play.core.review.ReviewInfo
import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.tasks.Task
import com.google.firebase.analytics.FirebaseAnalytics
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.preference.PreferenceManager;
class GoogleMainApplication : MainApplication() {
import com.google.android.play.core.review.ReviewManager;
import com.google.android.play.core.review.ReviewManagerFactory;
import com.google.android.play.core.tasks.Task;
import com.google.firebase.analytics.FirebaseAnalytics;
private var firebaseAnalytics: FirebaseAnalytics? = null
public class GoogleMainApplication extends MainApplication {
private static final String KEY_RATING_SHOWN = "ratingShown";
private static final long RATING_THRESHOLD = -24 * 60 * 60 * 1000L;
private FirebaseAnalytics firebaseAnalytics;
@Override
public void onCreate() {
super.onCreate();
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(TrackingService.ACTION_STARTED);
filter.addAction(TrackingService.ACTION_STOPPED);
registerReceiver(new ServiceReceiver(), filter);
override fun onCreate() {
super.onCreate()
firebaseAnalytics = FirebaseAnalytics.getInstance(this)
val filter = IntentFilter()
filter.addAction(TrackingService.ACTION_STARTED)
filter.addAction(TrackingService.ACTION_STOPPED)
registerReceiver(ServiceReceiver(), filter)
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
@Override
public void handleRatingFlow(@NonNull Activity activity) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean ratingShown = preferences.getBoolean(KEY_RATING_SHOWN, false);
long totalDuration = preferences.getLong(ServiceReceiver.KEY_DURATION, 0);
override fun handleRatingFlow(activity: Activity) {
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val ratingShown = preferences.getBoolean(KEY_RATING_SHOWN, false)
val totalDuration = preferences.getLong(ServiceReceiver.KEY_DURATION, 0)
if (!ratingShown && totalDuration > RATING_THRESHOLD) {
ReviewManager reviewManager = ReviewManagerFactory.create(activity);
reviewManager.requestReviewFlow().addOnCompleteListener(infoTask -> {
if (infoTask.isSuccessful()) {
Task<Void> flow = reviewManager.launchReviewFlow(activity, infoTask.getResult());
flow.addOnCompleteListener(flowTask -> {
preferences.edit().putBoolean(KEY_RATING_SHOWN, true).apply();
});
val reviewManager = ReviewManagerFactory.create(activity)
reviewManager.requestReviewFlow().addOnCompleteListener { infoTask: Task<ReviewInfo?> ->
if (infoTask.isSuccessful) {
val flow = reviewManager.launchReviewFlow(activity, infoTask.result)
flow.addOnCompleteListener { preferences.edit().putBoolean(KEY_RATING_SHOWN, true).apply() }
}
});
}
}
}
companion object {
private const val KEY_RATING_SHOWN = "ratingShown"
private const val RATING_THRESHOLD = -24 * 60 * 60 * 1000L
}
}
+40 -58
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,72 +13,54 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.annotation.SuppressLint;
import android.content.Context;
import android.location.Location;
import android.annotation.SuppressLint
import android.content.Context
import android.os.Looper
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnSuccessListener;
class GooglePositionProvider(context: Context, listener: PositionListener) : PositionProvider(context, listener) {
public class GooglePositionProvider extends PositionProvider {
private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
private FusedLocationProviderClient fusedLocationClient;
@Suppress("DEPRECATION", "MissingPermission")
override fun startUpdates() {
val locationRequest = LocationRequest()
locationRequest.priority = getPriority(preferences.getString(MainFragment.KEY_ACCURACY,"medium"))
locationRequest.interval = if (distance > 0 || angle > 0) MINIMUM_INTERVAL else interval
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
}
public GooglePositionProvider(Context context, PositionListener listener) {
super(context, listener);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
override fun stopUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
@SuppressLint("MissingPermission")
public void startUpdates() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setPriority(getPriority(preferences.getString(MainFragment.KEY_ACCURACY, "medium")));
locationRequest.setInterval(distance > 0 || angle > 0 ? MINIMUM_INTERVAL : interval);
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
public void stopUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback);
}
@SuppressLint("MissingPermission")
public void requestSingleLocation() {
fusedLocationClient.getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
if (location != null) {
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel(context)));
}
}
});
}
private static int getPriority(String accuracy) {
switch (accuracy) {
case "high":
return LocationRequest.PRIORITY_HIGH_ACCURACY;
case "low":
return LocationRequest.PRIORITY_LOW_POWER;
default:
return LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
}
}
private LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
for (Location location : locationResult.getLocations()) {
processLocation(location);
}
override fun requestSingleLocation() {
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
if (location != null) {
listener.onPositionUpdate(Position(deviceId, location, getBatteryLevel(context)))
}
}
};
}
private val locationCallback: LocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
for (location in locationResult.locations) {
processLocation(location)
}
}
}
private fun getPriority(accuracy: String?): Int {
return when (accuracy) {
"high" -> LocationRequest.PRIORITY_HIGH_ACCURACY
"low" -> LocationRequest.PRIORITY_LOW_POWER
else -> LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
}
}
}
+7 -7
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Context;
import android.content.Context
import org.traccar.client.PositionProvider.PositionListener
public class PositionProviderFactory {
object PositionProviderFactory {
public static PositionProvider create(Context context, PositionProvider.PositionListener listener) {
return new GooglePositionProvider(context, listener);
fun create(context: Context, listener: PositionListener): PositionProvider {
return GooglePositionProvider(context, listener)
}
}
+21 -26
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2020 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,37 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceManager;
class ServiceReceiver : BroadcastReceiver() {
public class ServiceReceiver extends BroadcastReceiver {
public static final String KEY_DURATION = "serviceTime";
private static long startTime = 0;
@Override
public void onReceive(Context context, Intent intent) {
if (TrackingService.ACTION_STARTED.equals(intent.getAction())) {
startTime = System.currentTimeMillis();
} else {
if (startTime > 0) {
updateTime(context, System.currentTimeMillis() - startTime);
startTime = 0;
}
override fun onReceive(context: Context, intent: Intent) {
if (TrackingService.ACTION_STARTED == intent.action) {
startTime = System.currentTimeMillis()
} else if (startTime > 0) {
updateTime(context, System.currentTimeMillis() - startTime)
startTime = 0
}
}
private void updateTime(Context context, long duration) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
long totalDuration = preferences.getLong(KEY_DURATION, 0);
preferences.edit().putLong(KEY_DURATION, totalDuration + duration).apply();
private fun updateTime(context: Context, duration: Long) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val totalDuration = preferences.getLong(KEY_DURATION, 0)
preferences.edit().putLong(KEY_DURATION, totalDuration + duration).apply()
}
companion object {
const val KEY_DURATION = "serviceTime"
private var startTime: Long = 0
}
}
-110
Просмотреть файл
@@ -1,110 +0,0 @@
/*
* Copyright 2019 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
import android.annotation.SuppressLint;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Looper;
public class AndroidPositionProvider extends PositionProvider implements LocationListener {
private LocationManager locationManager;
private String provider;
public AndroidPositionProvider(Context context, PositionListener listener) {
super(context, listener);
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
provider = getProvider(preferences.getString(MainFragment.KEY_ACCURACY, "medium"));
}
@SuppressLint("MissingPermission")
public void startUpdates() {
try {
locationManager.requestLocationUpdates(
provider, distance > 0 || angle > 0 ? MINIMUM_INTERVAL : interval, 0, this);
} catch (RuntimeException e) {
listener.onPositionError(e);
}
}
public void stopUpdates() {
locationManager.removeUpdates(this);
}
@SuppressLint("MissingPermission")
public void requestSingleLocation() {
try {
Location location = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
if (location != null) {
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel(context)));
} else {
locationManager.requestSingleUpdate(provider, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel(context)));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}, Looper.myLooper());
}
} catch (RuntimeException e) {
listener.onPositionError(e);
}
}
private static String getProvider(String accuracy) {
switch (accuracy) {
case "high":
return LocationManager.GPS_PROVIDER;
case "low":
return LocationManager.PASSIVE_PROVIDER;
default:
return LocationManager.NETWORK_PROVIDER;
}
}
@Override
public void onLocationChanged(Location location) {
processLocation(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}
+83
Просмотреть файл
@@ -0,0 +1,83 @@
/*
* Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client
import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
class AndroidPositionProvider(context: Context, listener: PositionListener) : PositionProvider(context, listener), LocationListener {
private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private val provider = getProvider(preferences.getString(MainFragment.KEY_ACCURACY, "medium"))
@SuppressLint("MissingPermission")
override fun startUpdates() {
try {
locationManager.requestLocationUpdates(
provider, if (distance > 0 || angle > 0) MINIMUM_INTERVAL else interval, 0f, this)
} catch (e: RuntimeException) {
listener.onPositionError(e)
}
}
override fun stopUpdates() {
locationManager.removeUpdates(this)
}
@Suppress("DEPRECATION", "MissingPermission")
override fun requestSingleLocation() {
try {
val location = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)
if (location != null) {
listener.onPositionUpdate(Position(deviceId, location, getBatteryLevel(context)))
} else {
locationManager.requestSingleUpdate(provider, object : LocationListener {
override fun onLocationChanged(location: Location) {
listener.onPositionUpdate(Position(deviceId, location, getBatteryLevel(context)))
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}, Looper.myLooper())
}
} catch (e: RuntimeException) {
listener.onPositionError(e)
}
}
override fun onLocationChanged(location: Location) {
processLocation(location)
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
private fun getProvider(accuracy: String?): String {
return when (accuracy) {
"high" -> LocationManager.GPS_PROVIDER
"low" -> LocationManager.PASSIVE_PROVIDER
else -> LocationManager.NETWORK_PROVIDER
}
}
}
+11 -12
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
* Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,20 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.content.Context
import android.content.Intent
import androidx.preference.PreferenceManager
public class AutostartReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
class AutostartReceiver : WakefulBroadcastReceiver() {
@Suppress("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context, intent: Intent) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
if (sharedPreferences.getBoolean(MainFragment.KEY_STATUS, false)) {
startWakefulForegroundService(context, new Intent(context, TrackingService.class));
startWakefulForegroundService(context, Intent(context, TrackingService::class.java))
}
}
+100 -126
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,168 +13,142 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
@file:Suppress("DEPRECATION", "StaticFieldLeak")
package org.traccar.client
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.content.ContentValues
import android.content.Context
import android.database.SQLException
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.os.AsyncTask
import java.sql.Date
import java.util.Date;
class DatabaseHelper(context: Context?) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 3;
public static final String DATABASE_NAME = "traccar.db";
public interface DatabaseHandler<T> {
void onComplete(boolean success, T result);
interface DatabaseHandler<T> {
fun onComplete(success: Boolean, result: T)
}
private static abstract class DatabaseAsyncTask<T> extends AsyncTask<Void, Void, T> {
private abstract class DatabaseAsyncTask<T>(val handler: DatabaseHandler<T>) : AsyncTask<Unit, Unit, T?>() {
private DatabaseHandler<T> handler;
private RuntimeException error;
private var error: RuntimeException? = null
public DatabaseAsyncTask(DatabaseHandler<T> handler) {
this.handler = handler;
}
@Override
protected T doInBackground(Void... params) {
try {
return executeMethod();
} catch (RuntimeException error) {
this.error = error;
return null;
override fun doInBackground(vararg params: Unit): T? {
return try {
executeMethod()
} catch (error: RuntimeException) {
this.error = error
null
}
}
protected abstract T executeMethod();
protected abstract fun executeMethod(): T
@Override
protected void onPostExecute(T result) {
handler.onComplete(error == null, result);
override fun onPostExecute(result: T?) {
result?.let { handler.onComplete(error == null, result) }
}
}
private SQLiteDatabase db;
private val db: SQLiteDatabase = writableDatabase
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
db = getWritableDatabase();
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(
"CREATE TABLE position (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"deviceId TEXT," +
"time INTEGER," +
"latitude REAL," +
"longitude REAL," +
"altitude REAL," +
"speed REAL," +
"course REAL," +
"accuracy REAL," +
"battery REAL," +
"mock INTEGER)"
)
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE position (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"deviceId TEXT," +
"time INTEGER," +
"latitude REAL," +
"longitude REAL," +
"altitude REAL," +
"speed REAL," +
"course REAL," +
"accuracy REAL," +
"battery REAL," +
"mock INTEGER)");
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS position;")
onCreate(db)
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS position;");
onCreate(db);
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS position;")
onCreate(db)
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS position;");
onCreate(db);
fun insertPosition(position: Position) {
val values = ContentValues()
values.put("deviceId", position.deviceId)
values.put("time", position.time.time)
values.put("latitude", position.latitude)
values.put("longitude", position.longitude)
values.put("altitude", position.altitude)
values.put("speed", position.speed)
values.put("course", position.course)
values.put("accuracy", position.accuracy)
values.put("battery", position.battery)
values.put("mock", if (position.mock) 1 else 0)
db.insertOrThrow("position", null, values)
}
public void insertPosition(Position position) {
ContentValues values = new ContentValues();
values.put("deviceId", position.getDeviceId());
values.put("time", position.getTime().getTime());
values.put("latitude", position.getLatitude());
values.put("longitude", position.getLongitude());
values.put("altitude", position.getAltitude());
values.put("speed", position.getSpeed());
values.put("course", position.getCourse());
values.put("accuracy", position.getAccuracy());
values.put("battery", position.getBattery());
values.put("mock", position.getMock() ? 1 : 0);
db.insertOrThrow("position", null, values);
}
public void insertPositionAsync(final Position position, DatabaseHandler<Void> handler) {
new DatabaseAsyncTask<Void>(handler) {
@Override
protected Void executeMethod() {
insertPosition(position);
return null;
fun insertPositionAsync(position: Position, handler: DatabaseHandler<Unit>) {
object : DatabaseAsyncTask<Unit>(handler) {
override fun executeMethod() {
insertPosition(position)
}
}.execute();
}.execute()
}
public Position selectPosition() {
Position position = new Position();
Cursor cursor = db.rawQuery("SELECT * FROM position ORDER BY id LIMIT 1", null);
try {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
position.setId(cursor.getLong(cursor.getColumnIndex("id")));
position.setDeviceId(cursor.getString(cursor.getColumnIndex("deviceId")));
position.setTime(new Date(cursor.getLong(cursor.getColumnIndex("time"))));
position.setLatitude(cursor.getDouble(cursor.getColumnIndex("latitude")));
position.setLongitude(cursor.getDouble(cursor.getColumnIndex("longitude")));
position.setAltitude(cursor.getDouble(cursor.getColumnIndex("altitude")));
position.setSpeed(cursor.getDouble(cursor.getColumnIndex("speed")));
position.setCourse(cursor.getDouble(cursor.getColumnIndex("course")));
position.setAccuracy(cursor.getDouble(cursor.getColumnIndex("accuracy")));
position.setBattery(cursor.getDouble(cursor.getColumnIndex("battery")));
position.setMock(cursor.getInt(cursor.getColumnIndex("mock")) > 0);
} else {
return null;
fun selectPosition(): Position? {
db.rawQuery("SELECT * FROM position ORDER BY id LIMIT 1", null).use { cursor ->
if (cursor.count > 0) {
cursor.moveToFirst()
return Position(
id = cursor.getLong(cursor.getColumnIndex("id")),
deviceId = cursor.getString(cursor.getColumnIndex("deviceId")),
time = Date(cursor.getLong(cursor.getColumnIndex("time"))),
latitude = cursor.getDouble(cursor.getColumnIndex("latitude")),
longitude = cursor.getDouble(cursor.getColumnIndex("longitude")),
altitude = cursor.getDouble(cursor.getColumnIndex("altitude")),
speed = cursor.getDouble(cursor.getColumnIndex("speed")),
course = cursor.getDouble(cursor.getColumnIndex("course")),
accuracy = cursor.getDouble(cursor.getColumnIndex("accuracy")),
battery = cursor.getDouble(cursor.getColumnIndex("battery")),
mock = cursor.getInt(cursor.getColumnIndex("mock")) > 0,
)
}
} finally {
cursor.close();
}
return position;
return null
}
public void selectPositionAsync(DatabaseHandler<Position> handler) {
new DatabaseAsyncTask<Position>(handler) {
@Override
protected Position executeMethod() {
return selectPosition();
fun selectPositionAsync(handler: DatabaseHandler<Position?>) {
object : DatabaseAsyncTask<Position?>(handler) {
override fun executeMethod(): Position? {
return selectPosition()
}
}.execute();
}.execute()
}
public void deletePosition(long id) {
if (db.delete("position", "id = ?", new String[] { String.valueOf(id) }) != 1) {
throw new SQLException();
fun deletePosition(id: Long) {
if (db.delete("position", "id = ?", arrayOf(id.toString())) != 1) {
throw SQLException()
}
}
public void deletePositionAsync(final long id, DatabaseHandler<Void> handler) {
new DatabaseAsyncTask<Void>(handler) {
@Override
protected Void executeMethod() {
deletePosition(id);
return null;
fun deletePositionAsync(id: Long, handler: DatabaseHandler<Unit>) {
object : DatabaseAsyncTask<Unit>(handler) {
override fun executeMethod() {
deletePosition(id)
}
}.execute();
}.execute()
}
companion object {
const val DATABASE_VERSION = 3
const val DATABASE_NAME = "traccar.db"
}
}
+17 -16
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,25 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
public class DialLaunchReceiver extends BroadcastReceiver {
class DialLaunchReceiver : BroadcastReceiver() {
private static final String LAUNCHER_NUMBER = "8722227"; // TRACCAR
@Override
public void onReceive(Context context, Intent intent) {
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
if (phoneNumber.equals(LAUNCHER_NUMBER)) {
setResultData(null);
Intent appIntent = new Intent(context, MainActivity.class);
appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(appIntent);
override fun onReceive(context: Context, intent: Intent) {
val phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
if (phoneNumber == LAUNCHER_NUMBER) {
resultData = null
val appIntent = Intent(context, MainActivity::class.java)
appIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(appIntent)
}
}
companion object {
private const val LAUNCHER_NUMBER = "8722227" // TRACCAR
}
}
+8 -10
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
public class MainActivity extends AppCompatActivity {
class MainActivity : AppCompatActivity() {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main)
}
}
+26 -47
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,67 +13,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import androidx.multidex.MultiDexApplication
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Notification
import android.graphics.Color
import android.os.Build
import android.app.Activity
import androidx.annotation.NonNull;
import androidx.multidex.MultiDexApplication;
import androidx.annotation.NonNull
public class MainApplication extends MultiDexApplication {
public static final String PRIMARY_CHANNEL = "default";
@Override
public void onCreate() {
super.onCreate();
System.setProperty("http.keepAliveDuration", String.valueOf(30 * 60 * 1000));
migrateLegacyPreferences(PreferenceManager.getDefaultSharedPreferences(this));
open class MainApplication : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
System.setProperty("http.keepAliveDuration", (30 * 60 * 1000).toString())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerChannel();
registerChannel()
}
}
@TargetApi(Build.VERSION_CODES.O)
private void registerChannel() {
NotificationChannel channel = new NotificationChannel(
PRIMARY_CHANNEL, getString(R.string.channel_default), NotificationManager.IMPORTANCE_LOW);
channel.setLightColor(Color.GREEN);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
private fun registerChannel() {
val channel = NotificationChannel(
PRIMARY_CHANNEL, getString(R.string.channel_default), NotificationManager.IMPORTANCE_LOW
)
channel.lightColor = Color.GREEN
channel.lockscreenVisibility = Notification.VISIBILITY_SECRET
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
}
private void migrateLegacyPreferences(SharedPreferences preferences) {
String port = preferences.getString("port", null);
if (port != null) {
String host = preferences.getString("address", getString(R.string.settings_url_default_value));
String scheme = preferences.getBoolean("secure", false) ? "https" : "http";
open fun handleRatingFlow(activity: Activity) {}
Uri.Builder builder = new Uri.Builder();
builder.scheme(scheme).encodedAuthority(host + ":" + port).build();
SharedPreferences.Editor editor = preferences.edit();
editor.putString(MainFragment.KEY_URL, builder.toString());
editor.remove("port");
editor.remove("address");
editor.remove("secure");
editor.apply();
}
}
public void handleRatingFlow(@NonNull Activity activity) {
companion object {
const val PRIMARY_CHANNEL = "default"
}
}
+194 -222
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,299 +13,271 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.Manifest;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.EditText;
import android.widget.Toast;
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.util.Log
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.webkit.URLUtil
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.preference.EditTextPreference
import androidx.preference.EditTextPreferenceDialogFragmentCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.TwoStatePreference
import java.util.*
import kotlin.collections.HashSet
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.preference.EditTextPreference;
import androidx.preference.EditTextPreferenceDialogFragmentCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.TwoStatePreference;
class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener {
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class MainFragment extends PreferenceFragmentCompat implements OnSharedPreferenceChangeListener {
private static final String TAG = MainFragment.class.getSimpleName();
private static final int ALARM_MANAGER_INTERVAL = 15000;
public static final String KEY_DEVICE = "id";
public static final String KEY_URL = "url";
public static final String KEY_INTERVAL = "interval";
public static final String KEY_DISTANCE = "distance";
public static final String KEY_ANGLE = "angle";
public static final String KEY_ACCURACY = "accuracy";
public static final String KEY_STATUS = "status";
public static final String KEY_BUFFER = "buffer";
public static final String KEY_WAKELOCK = "wakelock";
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
private SharedPreferences sharedPreferences;
private AlarmManager alarmManager;
private PendingIntent alarmIntent;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
private lateinit var sharedPreferences: SharedPreferences
private lateinit var alarmManager: AlarmManager
private lateinit var alarmIntent: PendingIntent
@SuppressLint("UnspecifiedImmutableFlag")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
if (BuildConfig.HIDDEN_APP && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
removeLauncherIcon();
removeLauncherIcon()
}
setHasOptionsMenu(true)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
setPreferencesFromResource(R.xml.preferences, rootKey)
initPreferences()
setHasOptionsMenu(true);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
setPreferencesFromResource(R.xml.preferences, rootKey);
initPreferences();
findPreference(KEY_DEVICE).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return newValue != null && !newValue.equals("");
findPreference<Preference>(KEY_DEVICE)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
newValue != null && newValue != ""
}
findPreference<Preference>(KEY_URL)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
newValue != null && validateServerURL(newValue.toString())
}
findPreference<Preference>(KEY_INTERVAL)?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
try {
newValue != null && (newValue as String).toInt() > 0
} catch (e: NumberFormatException) {
Log.w(TAG, e)
false
}
});
findPreference(KEY_URL).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return (newValue != null) && validateServerURL(newValue.toString());
}
val numberValidationListener = Preference.OnPreferenceChangeListener { _, newValue ->
try {
newValue != null && (newValue as String).toInt() >= 0
} catch (e: NumberFormatException) {
Log.w(TAG, e)
false
}
});
findPreference(KEY_INTERVAL).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue != null) {
try {
int value = Integer.parseInt((String) newValue);
return value > 0;
} catch (NumberFormatException e) {
Log.w(TAG, e);
}
}
return false;
}
});
Preference.OnPreferenceChangeListener numberValidationListener = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue != null) {
try {
int value = Integer.parseInt((String) newValue);
return value >= 0;
} catch (NumberFormatException e) {
Log.w(TAG, e);
}
}
return false;
}
};
findPreference(KEY_DISTANCE).setOnPreferenceChangeListener(numberValidationListener);
findPreference(KEY_ANGLE).setOnPreferenceChangeListener(numberValidationListener);
alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
alarmIntent = PendingIntent.getBroadcast(getActivity(), 0, new Intent(getActivity(), AutostartReceiver.class), 0);
}
findPreference<Preference>(KEY_DISTANCE)?.onPreferenceChangeListener = numberValidationListener
findPreference<Preference>(KEY_ANGLE)?.onPreferenceChangeListener = numberValidationListener
alarmManager = requireActivity().getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmIntent = PendingIntent.getBroadcast(activity, 0, Intent(activity, AutostartReceiver::class.java), 0)
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
startTrackingService(checkPermission = true, initialPermission = false)
}
}
public static class NumericEditTextPreferenceDialogFragment extends EditTextPreferenceDialogFragmentCompat {
class NumericEditTextPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() {
public static NumericEditTextPreferenceDialogFragment newInstance(String key) {
final NumericEditTextPreferenceDialogFragment fragment = new NumericEditTextPreferenceDialogFragment();
final Bundle bundle = new Bundle();
bundle.putString(ARG_KEY, key);
fragment.setArguments(bundle);
return fragment;
override fun onBindDialogView(view: View) {
val editText = view.findViewById<EditText>(android.R.id.edit)
editText.inputType = InputType.TYPE_CLASS_NUMBER
super.onBindDialogView(view)
}
@Override
protected void onBindDialogView(View view) {
EditText editText = view.findViewById(android.R.id.edit);
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
super.onBindDialogView(view);
companion object {
fun newInstance(key: String?): NumericEditTextPreferenceDialogFragment {
val fragment = NumericEditTextPreferenceDialogFragment()
val bundle = Bundle()
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
if (Arrays.asList(KEY_INTERVAL, KEY_DISTANCE, KEY_ANGLE).contains(preference.getKey())) {
final EditTextPreferenceDialogFragmentCompat f = NumericEditTextPreferenceDialogFragment.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG");
override fun onDisplayPreferenceDialog(preference: Preference) {
if (listOf(KEY_INTERVAL, KEY_DISTANCE, KEY_ANGLE).contains(preference.key)) {
val f: EditTextPreferenceDialogFragmentCompat =
NumericEditTextPreferenceDialogFragment.newInstance(preference.key)
f.setTargetFragment(this, 0)
f.show(requireFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG")
} else {
super.onDisplayPreferenceDialog(preference);
super.onDisplayPreferenceDialog(preference)
}
}
private void removeLauncherIcon() {
String className = MainActivity.class.getCanonicalName().replace(".MainActivity", ".Launcher");
ComponentName componentName = new ComponentName(getActivity().getPackageName(), className);
PackageManager packageManager = getActivity().getPackageManager();
private fun removeLauncherIcon() {
val className = MainActivity::class.java.canonicalName!!.replace(".MainActivity", ".Launcher")
val componentName = ComponentName(requireActivity().packageName, className)
val packageManager = requireActivity().packageManager
if (packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
packageManager.setComponentEnabledSetting(
componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.hidden_alert));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
val builder = AlertDialog.Builder(requireActivity())
builder.setIcon(android.R.drawable.ic_dialog_alert)
builder.setMessage(getString(R.string.hidden_alert))
builder.setPositiveButton(android.R.string.ok, null)
builder.show()
}
}
@Override
public void onResume() {
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
override fun onResume() {
super.onResume()
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
@Override
public void onPause() {
super.onPause();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
override fun onPause() {
super.onPause()
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
private void setPreferencesEnabled(boolean enabled) {
findPreference(KEY_DEVICE).setEnabled(enabled);
findPreference(KEY_URL).setEnabled(enabled);
findPreference(KEY_INTERVAL).setEnabled(enabled);
findPreference(KEY_DISTANCE).setEnabled(enabled);
findPreference(KEY_ANGLE).setEnabled(enabled);
findPreference(KEY_ACCURACY).setEnabled(enabled);
findPreference(KEY_BUFFER).setEnabled(enabled);
findPreference(KEY_WAKELOCK).setEnabled(enabled);
private fun setPreferencesEnabled(enabled: Boolean) {
findPreference<Preference>(KEY_DEVICE)?.isEnabled = enabled
findPreference<Preference>(KEY_URL)?.isEnabled = enabled
findPreference<Preference>(KEY_INTERVAL)?.isEnabled = enabled
findPreference<Preference>(KEY_DISTANCE)?.isEnabled = enabled
findPreference<Preference>(KEY_ANGLE)?.isEnabled = enabled
findPreference<Preference>(KEY_ACCURACY)?.isEnabled = enabled
findPreference<Preference>(KEY_BUFFER)?.isEnabled = enabled
findPreference<Preference>(KEY_WAKELOCK)?.isEnabled = enabled
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KEY_STATUS)) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (key == KEY_STATUS) {
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
startTrackingService(checkPermission = true, initialPermission = false)
} else {
stopTrackingService();
stopTrackingService()
}
((MainApplication) getActivity().getApplication()).handleRatingFlow(getActivity());
} else if (key.equals(KEY_DEVICE)) {
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
(requireActivity().application as MainApplication).handleRatingFlow(requireActivity())
} else if (key == KEY_DEVICE) {
findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
super.onCreateOptionsMenu(menu, inflater);
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.main, menu)
super.onCreateOptionsMenu(menu, inflater)
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.status) {
startActivity(new Intent(getActivity(), StatusActivity.class));
return true;
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.status) {
startActivity(Intent(activity, StatusActivity::class.java))
return true
}
return super.onOptionsItemSelected(item);
return super.onOptionsItemSelected(item)
}
private void initPreferences() {
PreferenceManager.setDefaultValues(getActivity(), R.xml.preferences, false);
private fun initPreferences() {
PreferenceManager.setDefaultValues(activity, R.xml.preferences, false)
if (!sharedPreferences.contains(KEY_DEVICE)) {
String id = String.valueOf(new Random().nextInt(900000) + 100000);
sharedPreferences.edit().putString(KEY_DEVICE, id).apply();
((EditTextPreference) findPreference(KEY_DEVICE)).setText(id);
val id = (Random().nextInt(900000) + 100000).toString()
sharedPreferences.edit().putString(KEY_DEVICE, id).apply()
findPreference<EditTextPreference>(KEY_DEVICE)?.text = id
}
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
}
private void startTrackingService(boolean checkPermission, boolean permission) {
private fun startTrackingService(checkPermission: Boolean, initialPermission: Boolean) {
var permission = initialPermission
if (checkPermission) {
Set<String> requiredPermissions = new HashSet<>();
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requiredPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
val requiredPermissions: MutableSet<String> = HashSet()
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requiredPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
permission = requiredPermissions.isEmpty();
permission = requiredPermissions.isEmpty()
if (!permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(requiredPermissions.toArray(new String[requiredPermissions.size()]), PERMISSIONS_REQUEST_LOCATION);
requestPermissions(
requiredPermissions.toTypedArray(),
PERMISSIONS_REQUEST_LOCATION
)
}
return;
return
}
}
if (permission) {
setPreferencesEnabled(false);
ContextCompat.startForegroundService(getContext(), new Intent(getActivity(), TrackingService.class));
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
ALARM_MANAGER_INTERVAL, ALARM_MANAGER_INTERVAL, alarmIntent);
setPreferencesEnabled(false)
ContextCompat.startForegroundService(requireContext(), Intent(activity, TrackingService::class.java))
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
ALARM_MANAGER_INTERVAL.toLong(), ALARM_MANAGER_INTERVAL.toLong(), alarmIntent
)
} else {
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply();
TwoStatePreference preference = findPreference(KEY_STATUS);
preference.setChecked(false);
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply()
val preference = findPreference<TwoStatePreference>(KEY_STATUS)
preference?.isChecked = false
}
}
private void stopTrackingService() {
alarmManager.cancel(alarmIntent);
getActivity().stopService(new Intent(getActivity(), TrackingService.class));
setPreferencesEnabled(true);
private fun stopTrackingService() {
alarmManager.cancel(alarmIntent)
requireActivity().stopService(Intent(activity, TrackingService::class.java))
setPreferencesEnabled(true)
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
boolean granted = true;
for (int result : grantResults) {
var granted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
granted = false;
break;
granted = false
break
}
}
startTrackingService(false, granted);
startTrackingService(false, granted)
}
}
private boolean validateServerURL(String userUrl) {
int port = Uri.parse(userUrl).getPort();
if (URLUtil.isValidUrl(userUrl) && (port == -1 || (port > 0 && port <= 65535))
&& (URLUtil.isHttpUrl(userUrl) || URLUtil.isHttpsUrl(userUrl))) {
return true;
private fun validateServerURL(userUrl: String): Boolean {
val port = Uri.parse(userUrl).port
if (
URLUtil.isValidUrl(userUrl) &&
(port == -1 || port in 1..65535) &&
(URLUtil.isHttpUrl(userUrl) || URLUtil.isHttpsUrl(userUrl))
) {
return true
}
Toast.makeText(getActivity(), R.string.error_msg_invalid_url, Toast.LENGTH_LONG).show();
return false;
Toast.makeText(activity, R.string.error_msg_invalid_url, Toast.LENGTH_LONG).show()
return false
}
companion object {
private val TAG = MainFragment::class.java.simpleName
private const val ALARM_MANAGER_INTERVAL = 15000
const val KEY_DEVICE = "id"
const val KEY_URL = "url"
const val KEY_INTERVAL = "interval"
const val KEY_DISTANCE = "distance"
const val KEY_ANGLE = "angle"
const val KEY_ACCURACY = "accuracy"
const val KEY_STATUS = "status"
const val KEY_BUFFER = "buffer"
const val KEY_WAKELOCK = "wakelock"
private const val PERMISSIONS_REQUEST_LOCATION = 2
}
}
+34 -40
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,56 +13,50 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
@file:Suppress("DEPRECATION")
package org.traccar.client
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.util.Log
public class NetworkManager extends BroadcastReceiver {
class NetworkManager(private val context: Context, private val handler: NetworkHandler?) : BroadcastReceiver() {
private static final String TAG = NetworkManager.class.getSimpleName();
private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private Context context;
private NetworkHandler handler;
private ConnectivityManager connectivityManager;
public NetworkManager(Context context, NetworkHandler handler) {
this.context = context;
this.handler = handler;
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
interface NetworkHandler {
fun onNetworkUpdate(isOnline: Boolean)
}
public interface NetworkHandler {
void onNetworkUpdate(boolean isOnline);
val isOnline: Boolean
get() {
val activeNetwork = connectivityManager.activeNetworkInfo
return activeNetwork != null && activeNetwork.isConnectedOrConnecting
}
fun start() {
val filter = IntentFilter()
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiver(this, filter)
}
public boolean isOnline() {
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
fun stop() {
context.unregisterReceiver(this)
}
public void start() {
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(this, filter);
}
public void stop() {
context.unregisterReceiver(this);
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION) && handler != null) {
boolean isOnline = isOnline();
Log.i(TAG, "network " + (isOnline ? "on" : "off"));
handler.onNetworkUpdate(isOnline);
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION && handler != null) {
val isOnline = isOnline
Log.i(TAG, "network " + if (isOnline) "on" else "off")
handler.onNetworkUpdate(isOnline)
}
}
companion object {
private val TAG = NetworkManager::class.java.simpleName
}
}
+31 -138
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,144 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.location.Location;
import android.location.LocationManager;
import android.os.Build;
import android.location.Location
import android.location.LocationManager
import android.os.Build
import java.util.*
import java.util.Date;
public class Position {
public Position() {
}
public Position(String deviceId, Location location, double battery) {
this.deviceId = deviceId;
time = new Date(location.getTime());
latitude = location.getLatitude();
longitude = location.getLongitude();
altitude = location.getAltitude();
speed = location.getSpeed() * 1.943844; // speed in knots
course = location.getBearing();
if (location.getProvider() != null && !location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
accuracy = location.getAccuracy();
}
this.battery = battery;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
this.mock = location.isFromMockProvider();
}
}
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
private String deviceId;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
private double latitude;
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
private double longitude;
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
private double altitude;
public double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
this.altitude = altitude;
}
private double speed;
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
private double course;
public double getCourse() {
return course;
}
public void setCourse(double course) {
this.course = course;
}
private double accuracy;
public double getAccuracy() {
return accuracy;
}
public void setAccuracy(double accuracy) {
this.accuracy = accuracy;
}
private double battery;
public double getBattery() {
return battery;
}
public void setBattery(double battery) {
this.battery = battery;
}
private boolean mock;
public boolean getMock() {
return mock;
}
public void setMock(boolean mock) {
this.mock = mock;
}
data class Position(
val id: Long = 0,
val deviceId: String,
val time: Date,
val latitude: Double = 0.0,
val longitude: Double = 0.0,
val altitude: Double = 0.0,
val speed: Double = 0.0,
val course: Double = 0.0,
val accuracy: Double = 0.0,
val battery: Double = 0.0,
val mock: Boolean = false,
) {
constructor(deviceId: String, location: Location, battery: Double) : this(
deviceId = deviceId,
time = Date(location.time),
latitude = location.latitude,
longitude = location.longitude,
altitude = location.altitude,
speed = location.speed * 1.943844, // speed in knots
course = location.bearing.toDouble(),
accuracy = if (location.provider != null && location.provider != LocationManager.GPS_PROVIDER) location.accuracy.toDouble() else 0.0,
battery = battery,
mock = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) location.isFromMockProvider else false,
)
}
+48 -61
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,79 +13,66 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.BatteryManager;
import android.preference.PreferenceManager;
import android.util.Log;
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.location.Location
import android.os.BatteryManager
import androidx.preference.PreferenceManager
import android.util.Log
import kotlin.math.abs
public abstract class PositionProvider {
abstract class PositionProvider(
protected val context: Context,
protected val listener: PositionListener,
) {
private static final String TAG = PositionProvider.class.getSimpleName();
protected static final int MINIMUM_INTERVAL = 1000;
public interface PositionListener {
void onPositionUpdate(Position position);
void onPositionError(Throwable error);
interface PositionListener {
fun onPositionUpdate(position: Position)
fun onPositionError(error: Throwable)
}
protected final PositionListener listener;
protected var preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
protected var deviceId = preferences.getString(MainFragment.KEY_DEVICE, "undefined")!!
protected var interval = preferences.getString(MainFragment.KEY_INTERVAL, "600")!!.toLong() * 1000
protected var distance: Double = preferences.getString(MainFragment.KEY_DISTANCE, "0")!!.toInt().toDouble()
protected var angle: Double = preferences.getString(MainFragment.KEY_ANGLE, "0")!!.toInt().toDouble()
private var lastLocation: Location? = null
protected final Context context;
protected SharedPreferences preferences;
abstract fun startUpdates()
abstract fun stopUpdates()
abstract fun requestSingleLocation()
protected String deviceId;
protected long interval;
protected double distance;
protected double angle;
protected Location lastLocation;
public PositionProvider(Context context, PositionListener listener) {
this.context = context;
this.listener = listener;
preferences = PreferenceManager.getDefaultSharedPreferences(context);
deviceId = preferences.getString(MainFragment.KEY_DEVICE, "undefined");
interval = Long.parseLong(preferences.getString(MainFragment.KEY_INTERVAL, "600")) * 1000;
distance = Integer.parseInt(preferences.getString(MainFragment.KEY_DISTANCE, "0"));
angle = Integer.parseInt(preferences.getString(MainFragment.KEY_ANGLE, "0"));
}
public abstract void startUpdates();
public abstract void stopUpdates();
public abstract void requestSingleLocation();
protected void processLocation(Location location) {
if (location != null && (lastLocation == null
|| location.getTime() - lastLocation.getTime() >= interval
|| distance > 0 && location.distanceTo(lastLocation) >= distance
|| angle > 0 && Math.abs(location.getBearing() - lastLocation.getBearing()) >= angle)) {
Log.i(TAG, "location new");
lastLocation = location;
listener.onPositionUpdate(new Position(deviceId, location, getBatteryLevel(context)));
protected fun processLocation(location: Location?) {
if (location != null &&
(lastLocation == null || location.time - lastLocation!!.time >= interval || distance > 0
&& location.distanceTo(lastLocation) >= distance || angle > 0
&& abs(location.bearing - lastLocation!!.bearing) >= angle)
) {
Log.i(TAG, "location new")
lastLocation = location
listener.onPositionUpdate(Position(deviceId, location, getBatteryLevel(context)))
} else {
Log.i(TAG, location != null ? "location ignored" : "location nil");
Log.i(TAG, if (location != null) "location ignored" else "location nil")
}
}
protected static double getBatteryLevel(Context context) {
Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
protected fun getBatteryLevel(context: Context): Double {
val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
if (batteryIntent != null) {
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
return (level * 100.0) / scale;
val level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
val scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1)
return level * 100.0 / scale
}
return 0;
return 0.0
}
companion object {
private val TAG = PositionProvider::class.java.simpleName
const val MINIMUM_INTERVAL: Long = 1000
}
}
+20 -27
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
* Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,37 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.net.Uri;
import android.net.Uri
public class ProtocolFormatter {
object ProtocolFormatter {
public static String formatRequest(String url, Position position) {
return formatRequest(url, position, null);
}
public static String formatRequest(String url, Position position, String alarm) {
Uri serverUrl = Uri.parse(url);
Uri.Builder builder = serverUrl.buildUpon()
.appendQueryParameter("id", position.getDeviceId())
.appendQueryParameter("timestamp", String.valueOf(position.getTime().getTime() / 1000))
.appendQueryParameter("lat", String.valueOf(position.getLatitude()))
.appendQueryParameter("lon", String.valueOf(position.getLongitude()))
.appendQueryParameter("speed", String.valueOf(position.getSpeed()))
.appendQueryParameter("bearing", String.valueOf(position.getCourse()))
.appendQueryParameter("altitude", String.valueOf(position.getAltitude()))
.appendQueryParameter("accuracy", String.valueOf(position.getAccuracy()))
.appendQueryParameter("batt", String.valueOf(position.getBattery()));
if (position.getMock()) {
builder.appendQueryParameter("mock", String.valueOf(position.getMock()));
fun formatRequest(url: String, position: Position, alarm: String? = null): String {
val serverUrl = Uri.parse(url)
val builder = serverUrl.buildUpon()
.appendQueryParameter("id", position.deviceId)
.appendQueryParameter("timestamp", (position.time.time / 1000).toString())
.appendQueryParameter("lat", position.latitude.toString())
.appendQueryParameter("lon", position.longitude.toString())
.appendQueryParameter("speed", position.speed.toString())
.appendQueryParameter("bearing", position.course.toString())
.appendQueryParameter("altitude", position.altitude.toString())
.appendQueryParameter("accuracy", position.accuracy.toString())
.appendQueryParameter("batt", position.battery.toString())
if (position.mock) {
builder.appendQueryParameter("mock", position.mock.toString())
}
if (alarm != null) {
builder.appendQueryParameter("alarm", alarm);
builder.appendQueryParameter("alarm", alarm)
}
return builder.build().toString();
return builder.build().toString()
}
}
+44 -56
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,71 +13,59 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
@file:Suppress("DEPRECATION")
package org.traccar.client
import android.os.AsyncTask;
import android.util.Log;
import android.os.AsyncTask
import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
object RequestManager {
public class RequestManager {
private const val TIMEOUT = 15 * 1000
private static final int TIMEOUT = 15 * 1000;
public interface RequestHandler {
void onComplete(boolean success);
}
private static class RequestAsyncTask extends AsyncTask<String, Void, Boolean> {
private RequestHandler handler;
public RequestAsyncTask(RequestHandler handler) {
this.handler = handler;
}
@Override
protected Boolean doInBackground(String... request) {
return sendRequest(request[0]);
}
@Override
protected void onPostExecute(Boolean result) {
handler.onComplete(result);
}
}
public static boolean sendRequest(String request) {
InputStream inputStream = null;
try {
URL url = new URL(request);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(TIMEOUT);
connection.setConnectTimeout(TIMEOUT);
connection.setRequestMethod("POST");
connection.connect();
inputStream = connection.getInputStream();
while (inputStream.read() != -1);
return true;
} catch (IOException error) {
return false;
fun sendRequest(request: String?): Boolean {
var inputStream: InputStream? = null
return try {
val url = URL(request)
val connection = url.openConnection() as HttpURLConnection
connection.readTimeout = TIMEOUT
connection.connectTimeout = TIMEOUT
connection.requestMethod = "POST"
connection.connect()
inputStream = connection.inputStream
while (inputStream.read() != -1) {}
true
} catch (error: IOException) {
false
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException secondError) {
Log.w(RequestManager.class.getSimpleName(), secondError);
inputStream?.close()
} catch (secondError: IOException) {
Log.w(RequestManager::class.java.simpleName, secondError)
}
}
}
public static void sendRequestAsync(String request, RequestHandler handler) {
RequestAsyncTask task = new RequestAsyncTask(handler);
task.execute(request);
fun sendRequestAsync(request: String, handler: RequestHandler) {
RequestAsyncTask(handler).execute(request)
}
interface RequestHandler {
fun onComplete(success: Boolean)
}
private class RequestAsyncTask(private val handler: RequestHandler) : AsyncTask<String, Unit, Boolean>() {
override fun doInBackground(vararg request: String): Boolean {
return sendRequest(request[0])
}
override fun onPostExecute(result: Boolean) {
handler.onComplete(result)
}
}
}
+94 -120
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,152 +13,126 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.preference.PreferenceManager
import org.traccar.client.PositionProvider.PositionListener
import org.traccar.client.ProtocolFormatter.formatRequest
import org.traccar.client.RequestManager.RequestHandler
import org.traccar.client.RequestManager.sendRequestAsync
import androidx.annotation.DrawableRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.drawable.IconCompat;
class ShortcutActivity : AppCompatActivity() {
public class ShortcutActivity extends AppCompatActivity {
public static final String EXTRA_ACTION = "action";
public static final String ACTION_START = "start";
public static final String ACTION_STOP = "stop";
public static final String ACTION_SOS = "sos";
private static final String ALARM_SOS = "sos";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!executeAction(getIntent())) {
setContentView(R.layout.list);
final String[] items = new String[] {
getString(R.string.shortcut_start),
getString(R.string.shortcut_stop),
getString(R.string.shortcut_sos)
};
ListView listView = findViewById(android.R.id.list);
listView.setAdapter(new ArrayAdapter<>(
this, android.R.layout.simple_list_item_1, items));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 0:
setShortcutResult(items[position], R.mipmap.ic_start, ACTION_START);
break;
case 1:
setShortcutResult(items[position], R.mipmap.ic_stop, ACTION_STOP);
break;
case 2:
setShortcutResult(items[position], R.mipmap.ic_sos, ACTION_SOS);
break;
}
finish();
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!executeAction(intent)) {
setContentView(R.layout.list)
val items = arrayOf(
getString(R.string.shortcut_start),
getString(R.string.shortcut_stop),
getString(R.string.shortcut_sos)
)
val listView = findViewById<ListView>(android.R.id.list)
listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
listView.onItemClickListener = OnItemClickListener { _, _, position, _ ->
when (position) {
0 -> setShortcutResult(items[position], R.mipmap.ic_start, ACTION_START)
1 -> setShortcutResult(items[position], R.mipmap.ic_stop, ACTION_STOP)
2 -> setShortcutResult(items[position], R.mipmap.ic_sos, ACTION_SOS)
}
});
finish()
}
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
executeAction(intent);
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
executeAction(intent)
}
private void setShortcutResult(String label, @DrawableRes int iconResId, String action) {
Intent intent = new Intent(Intent.ACTION_DEFAULT, null, this, ShortcutActivity.class);
intent.putExtra(EXTRA_ACTION, action);
ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, action)
.setShortLabel(label)
.setIcon(IconCompat.createWithResource(this, iconResId))
.setIntent(intent)
.build();
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut));
private fun setShortcutResult(label: String, @DrawableRes iconResId: Int, action: String) {
val intent = Intent(Intent.ACTION_DEFAULT, null, this, ShortcutActivity::class.java)
intent.putExtra(EXTRA_ACTION, action)
val shortcut = ShortcutInfoCompat.Builder(this, action)
.setShortLabel(label)
.setIcon(IconCompat.createWithResource(this, iconResId))
.setIntent(intent)
.build()
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut))
}
@SuppressWarnings("MissingPermission")
private void sendAlarm() {
PositionProviderFactory.create(this, new PositionProvider.PositionListener() {
@Override
public void onPositionUpdate(Position position) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ShortcutActivity.this);
String request = ProtocolFormatter.formatRequest(
preferences.getString(MainFragment.KEY_URL, null), position, ALARM_SOS);
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
@Override
public void onComplete(boolean success) {
private fun sendAlarm() {
PositionProviderFactory.create(this, object : PositionListener {
override fun onPositionUpdate(position: Position) {
val preferences = PreferenceManager.getDefaultSharedPreferences(this@ShortcutActivity)
val request = formatRequest(preferences.getString(MainFragment.KEY_URL, null)!!, position, ALARM_SOS)
sendRequestAsync(request, object : RequestHandler {
override fun onComplete(success: Boolean) {
if (success) {
Toast.makeText(ShortcutActivity.this, R.string.status_send_success, Toast.LENGTH_SHORT).show();
Toast.makeText(this@ShortcutActivity, R.string.status_send_success, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(ShortcutActivity.this, R.string.status_send_fail, Toast.LENGTH_SHORT).show();
Toast.makeText(this@ShortcutActivity, R.string.status_send_fail, Toast.LENGTH_SHORT).show()
}
}
});
})
}
@Override
public void onPositionError(Throwable error) {
Toast.makeText(ShortcutActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
override fun onPositionError(error: Throwable) {
Toast.makeText(this@ShortcutActivity, error.message, Toast.LENGTH_LONG).show()
}
}).requestSingleLocation();
}).requestSingleLocation()
}
private boolean executeAction(Intent intent) {
String action;
if (intent.hasExtra("shortcutAction")) {
action = intent.getBooleanExtra("shortcutAction", false)
? ACTION_START : ACTION_STOP;
private fun executeAction(intent: Intent): Boolean {
val action: String? = if (intent.hasExtra("shortcutAction")) {
if (intent.getBooleanExtra("shortcutAction", false)) ACTION_START else ACTION_STOP
} else {
action = intent.getStringExtra(EXTRA_ACTION);
intent.getStringExtra(EXTRA_ACTION)
}
if (action != null) {
switch (action) {
case ACTION_START:
PreferenceManager.getDefaultSharedPreferences(this)
.edit().putBoolean(MainFragment.KEY_STATUS, true).apply();
ContextCompat.startForegroundService(this, new Intent(this, TrackingService.class));
Toast.makeText(this, R.string.status_service_create, Toast.LENGTH_SHORT).show();
break;
case ACTION_STOP:
PreferenceManager.getDefaultSharedPreferences(this)
.edit().putBoolean(MainFragment.KEY_STATUS, false).apply();
stopService(new Intent(this, TrackingService.class));
Toast.makeText(this, R.string.status_service_destroy, Toast.LENGTH_SHORT).show();
break;
case ACTION_SOS:
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
sendAlarm();
when (action) {
ACTION_START -> {
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(MainFragment.KEY_STATUS, true).apply()
ContextCompat.startForegroundService(this, Intent(this, TrackingService::class.java))
Toast.makeText(this, R.string.status_service_create, Toast.LENGTH_SHORT).show()
}
ACTION_STOP -> {
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(MainFragment.KEY_STATUS, false).apply()
stopService(Intent(this, TrackingService::class.java))
Toast.makeText(this, R.string.status_service_destroy, Toast.LENGTH_SHORT).show()
}
ACTION_SOS -> {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
sendAlarm()
} else {
Toast.makeText(this, R.string.status_send_fail, Toast.LENGTH_SHORT).show();
Toast.makeText(this, R.string.status_send_fail, Toast.LENGTH_SHORT).show()
}
break;
}
}
finish();
finish()
}
return action != null;
return action != null
}
companion object {
const val EXTRA_ACTION = "action"
const val ACTION_START = "start"
const val ACTION_STOP = "stop"
const val ACTION_SOS = "sos"
private const val ALARM_SOS = "sos"
}
}
+63 -71
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
* Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,82 +13,74 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import java.text.DateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import androidx.appcompat.app.AppCompatActivity
import android.widget.ArrayAdapter
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.ListView
import java.text.DateFormat
import java.util.*
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
class StatusActivity : AppCompatActivity() {
public class StatusActivity extends AppCompatActivity {
private var adapter: ArrayAdapter<String>? = null
private static final int LIMIT = 20;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.list)
adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages)
val listView = findViewById<ListView>(android.R.id.list)
listView.adapter = adapter
adapter?.let { adapters.add(it) }
}
private static final LinkedList<String> messages = new LinkedList<>();
private static final Set<ArrayAdapter<String>> adapters = new HashSet<>();
override fun onDestroy() {
adapters.remove(adapter)
super.onDestroy()
}
private static void notifyAdapters() {
for (ArrayAdapter<String> adapter : adapters) {
adapter.notifyDataSetChanged();
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.status, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.clear) {
clearMessages()
return true
}
return super.onOptionsItemSelected(item)
}
companion object {
private const val LIMIT = 20
private val messages = LinkedList<String>()
private val adapters: MutableSet<ArrayAdapter<String>> = HashSet()
private fun notifyAdapters() {
for (adapter in adapters) {
adapter.notifyDataSetChanged()
}
}
fun addMessage(originalMessage: String) {
var message = originalMessage
val format = DateFormat.getTimeInstance(DateFormat.MEDIUM)
message = format.format(Date()) + " - " + message
messages.add(message)
while (messages.size > LIMIT) {
messages.removeFirst()
}
notifyAdapters()
}
fun clearMessages() {
messages.clear()
notifyAdapters()
}
}
public static void addMessage(String message) {
DateFormat format = DateFormat.getTimeInstance(DateFormat.MEDIUM);
message = format.format(new Date()) + " - " + message;
messages.add(message);
while (messages.size() > LIMIT) {
messages.removeFirst();
}
notifyAdapters();
}
public static void clearMessages() {
messages.clear();
notifyAdapters();
}
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages);
ListView listView = findViewById(android.R.id.list);
listView.setAdapter(adapter);
adapters.add(adapter);
}
@Override
protected void onDestroy() {
adapters.remove(adapter);
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.status, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.clear) {
clearMessages();
return true;
}
return super.onOptionsItemSelected(item);
}
}
+99 -119
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,92 +13,73 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.content.Context
import org.traccar.client.ProtocolFormatter.formatRequest
import org.traccar.client.RequestManager.sendRequestAsync
import org.traccar.client.PositionProvider.PositionListener
import org.traccar.client.NetworkManager.NetworkHandler
import android.os.Handler
import android.os.Looper
import androidx.preference.PreferenceManager
import android.util.Log
import org.traccar.client.DatabaseHelper.DatabaseHandler
import org.traccar.client.RequestManager.RequestHandler
public class TrackingController implements PositionProvider.PositionListener, NetworkManager.NetworkHandler {
class TrackingController(private val context: Context) : PositionListener, NetworkHandler {
private static final String TAG = TrackingController.class.getSimpleName();
private static final int RETRY_DELAY = 30 * 1000;
private val handler = Handler(Looper.getMainLooper())
private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
private val positionProvider = PositionProviderFactory.create(context, this)
private val databaseHelper = DatabaseHelper(context)
private val networkManager = NetworkManager(context, this)
private boolean isOnline;
private boolean isWaiting;
private val url: String = preferences.getString(MainFragment.KEY_URL, context.getString(R.string.settings_url_default_value))!!
private val buffer: Boolean = preferences.getBoolean(MainFragment.KEY_BUFFER, true)
private Context context;
private Handler handler;
private SharedPreferences preferences;
private var isOnline = networkManager.isOnline
private var isWaiting = false
private String url;
private boolean buffer;
private PositionProvider positionProvider;
private DatabaseHelper databaseHelper;
private NetworkManager networkManager;
public TrackingController(Context context) {
this.context = context;
handler = new Handler();
preferences = PreferenceManager.getDefaultSharedPreferences(context);
positionProvider = PositionProviderFactory.create(context, this);
databaseHelper = new DatabaseHelper(context);
networkManager = new NetworkManager(context, this);
isOnline = networkManager.isOnline();
url = preferences.getString(MainFragment.KEY_URL, context.getString(R.string.settings_url_default_value));
buffer = preferences.getBoolean(MainFragment.KEY_BUFFER, true);
}
public void start() {
fun start() {
if (isOnline) {
read();
read()
}
try {
positionProvider.startUpdates();
} catch (SecurityException e) {
Log.w(TAG, e);
positionProvider.startUpdates()
} catch (e: SecurityException) {
Log.w(TAG, e)
}
networkManager.start();
networkManager.start()
}
public void stop() {
networkManager.stop();
fun stop() {
networkManager.stop()
try {
positionProvider.stopUpdates();
} catch (SecurityException e) {
Log.w(TAG, e);
positionProvider.stopUpdates()
} catch (e: SecurityException) {
Log.w(TAG, e)
}
handler.removeCallbacksAndMessages(null);
handler.removeCallbacksAndMessages(null)
}
@Override
public void onPositionUpdate(Position position) {
StatusActivity.addMessage(context.getString(R.string.status_location_update));
if (position != null) {
if (buffer) {
write(position);
} else {
send(position);
}
override fun onPositionUpdate(position: Position) {
StatusActivity.addMessage(context.getString(R.string.status_location_update))
if (buffer) {
write(position)
} else {
send(position)
}
}
@Override
public void onPositionError(Throwable error) {
}
@Override
public void onNetworkUpdate(boolean isOnline) {
int message = isOnline ? R.string.status_network_online : R.string.status_network_offline;
StatusActivity.addMessage(context.getString(message));
override fun onPositionError(error: Throwable) {}
override fun onNetworkUpdate(isOnline: Boolean) {
val message = if (isOnline) R.string.status_network_online else R.string.status_network_offline
StatusActivity.addMessage(context.getString(message))
if (!this.isOnline && isOnline) {
read();
read()
}
this.isOnline = isOnline;
this.isOnline = isOnline
}
//
@@ -109,98 +90,97 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
// read -> send -> retry -> read -> send
//
private void log(String action, Position position) {
private fun log(action: String, position: Position?) {
var formattedAction: String = action
if (position != null) {
action += " (" +
"id:" + position.getId() +
" time:" + position.getTime().getTime() / 1000 +
" lat:" + position.getLatitude() +
" lon:" + position.getLongitude() + ")";
formattedAction +=
" (id:" + position.id +
" time:" + position.time.time / 1000 +
" lat:" + position.latitude +
" lon:" + position.longitude + ")"
}
Log.d(TAG, action);
Log.d(TAG, formattedAction)
}
private void write(Position position) {
log("write", position);
databaseHelper.insertPositionAsync(position, new DatabaseHelper.DatabaseHandler<Void>() {
@Override
public void onComplete(boolean success, Void result) {
private fun write(position: Position) {
log("write", position)
databaseHelper.insertPositionAsync(position, object : DatabaseHandler<Unit> {
override fun onComplete(success: Boolean, result: Unit) {
if (success) {
if (isOnline && isWaiting) {
read();
isWaiting = false;
read()
isWaiting = false
}
}
}
});
})
}
private void read() {
log("read", null);
databaseHelper.selectPositionAsync(new DatabaseHelper.DatabaseHandler<Position>() {
@Override
public void onComplete(boolean success, Position result) {
private fun read() {
log("read", null)
databaseHelper.selectPositionAsync(object : DatabaseHandler<Position?> {
override fun onComplete(success: Boolean, result: Position?) {
if (success) {
if (result != null) {
if (result.getDeviceId().equals(preferences.getString(MainFragment.KEY_DEVICE, null))) {
send(result);
if (result.deviceId == preferences.getString(MainFragment.KEY_DEVICE, null)) {
send(result)
} else {
delete(result);
delete(result)
}
} else {
isWaiting = true;
isWaiting = true
}
} else {
retry();
retry()
}
}
});
})
}
private void delete(Position position) {
log("delete", position);
databaseHelper.deletePositionAsync(position.getId(), new DatabaseHelper.DatabaseHandler<Void>() {
@Override
public void onComplete(boolean success, Void result) {
private fun delete(position: Position) {
log("delete", position)
databaseHelper.deletePositionAsync(position.id, object : DatabaseHandler<Unit> {
override fun onComplete(success: Boolean, result: Unit) {
if (success) {
read();
read()
} else {
retry();
retry()
}
}
});
})
}
private void send(final Position position) {
log("send", position);
String request = ProtocolFormatter.formatRequest(url, position);
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
@Override
public void onComplete(boolean success) {
private fun send(position: Position) {
log("send", position)
val request = formatRequest(url, position)
sendRequestAsync(request, object : RequestHandler {
override fun onComplete(success: Boolean) {
if (success) {
if (buffer) {
delete(position);
delete(position)
}
} else {
StatusActivity.addMessage(context.getString(R.string.status_send_fail));
StatusActivity.addMessage(context.getString(R.string.status_send_fail))
if (buffer) {
retry();
retry()
}
}
}
});
})
}
private void retry() {
log("retry", null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isOnline) {
read();
}
private fun retry() {
log("retry", null)
handler.postDelayed({
if (isOnline) {
read()
}
}, RETRY_DELAY);
}, RETRY_DELAY.toLong())
}
companion object {
private val TAG = TrackingController::class.java.simpleName
private const val RETRY_DELAY = 30 * 1000
}
}
+84 -95
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,128 +13,117 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import android.Manifest
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Notification
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import android.os.PowerManager.WakeLock
import android.provider.Settings
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;
class TrackingService : Service() {
public class TrackingService extends Service {
private var wakeLock: WakeLock? = null
private var trackingController: TrackingController? = null
public static final String ACTION_STARTED = "org.traccar.action.SERVICE_STARTED";
public static final String ACTION_STOPPED = "org.traccar.action.SERVICE_STOPPED";
private static final String TAG = TrackingService.class.getSimpleName();
private static final int NOTIFICATION_ID = 1;
private PowerManager.WakeLock wakeLock;
private TrackingController trackingController;
private static Notification createNotification(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, MainApplication.PRIMARY_CHANNEL)
.setSmallIcon(R.drawable.ic_stat_notify)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE);
Intent intent;
if (!BuildConfig.HIDDEN_APP) {
intent = new Intent(context, MainActivity.class);
builder
.setContentTitle(context.getString(R.string.settings_status_on_summary))
.setTicker(context.getString(R.string.settings_status_on_summary))
.setColor(ContextCompat.getColor(context, R.color.primary_dark));
} else {
intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
}
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
return builder.build();
}
public static class HideNotificationService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
class HideNotificationService : Service() {
override fun onBind(intent: Intent): IBinder? {
return null
}
@Override
public void onCreate() {
startForeground(NOTIFICATION_ID, createNotification(this));
stopForeground(true);
override fun onCreate() {
startForeground(NOTIFICATION_ID, createNotification(this))
stopForeground(true)
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
stopSelfResult(startId);
return START_NOT_STICKY;
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
stopSelfResult(startId)
return START_NOT_STICKY
}
}
@SuppressLint("WakelockTimeout")
@Override
public void onCreate() {
Log.i(TAG, "service create");
sendBroadcast(new Intent(ACTION_STARTED));
StatusActivity.addMessage(getString(R.string.status_service_create));
override fun onCreate() {
Log.i(TAG, "service create")
startForeground(NOTIFICATION_ID, createNotification(this));
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
sendBroadcast(Intent(ACTION_STARTED))
StatusActivity.addMessage(getString(R.string.status_service_create))
startForeground(NOTIFICATION_ID, createNotification(this))
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(MainFragment.KEY_WAKELOCK, true)) {
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
wakeLock.acquire();
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, javaClass.name)
wakeLock?.acquire()
}
trackingController = new TrackingController(this);
trackingController.start();
trackingController = TrackingController(this)
trackingController?.start()
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(this, new Intent(this, HideNotificationService.class));
ContextCompat.startForegroundService(this, Intent(this, HideNotificationService::class.java))
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
override fun onBind(intent: Intent): IBinder? {
return null
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
AutostartReceiver.completeWakefulIntent(intent);
}
return START_STICKY;
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
WakefulBroadcastReceiver.completeWakefulIntent(intent)
return START_STICKY
}
@Override
public void onDestroy() {
Log.i(TAG, "service destroy");
sendBroadcast(new Intent(ACTION_STOPPED));
StatusActivity.addMessage(getString(R.string.status_service_destroy));
stopForeground(true);
if (wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
}
if (trackingController != null) {
trackingController.stop();
override fun onDestroy() {
Log.i(TAG, "service destroy")
sendBroadcast(Intent(ACTION_STOPPED))
StatusActivity.addMessage(getString(R.string.status_service_destroy))
stopForeground(true)
if (wakeLock?.isHeld == true) {
wakeLock?.release()
}
trackingController?.stop()
}
companion object {
const val ACTION_STARTED = "org.traccar.action.SERVICE_STARTED"
const val ACTION_STOPPED = "org.traccar.action.SERVICE_STOPPED"
private val TAG = TrackingService::class.java.simpleName
private const val NOTIFICATION_ID = 1
@SuppressLint("UnspecifiedImmutableFlag")
private fun createNotification(context: Context): Notification {
val builder = NotificationCompat.Builder(context, MainApplication.PRIMARY_CHANNEL)
.setSmallIcon(R.drawable.ic_stat_notify)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
val intent: Intent
if (!BuildConfig.HIDDEN_APP) {
intent = Intent(context, MainActivity::class.java)
builder
.setContentTitle(context.getString(R.string.settings_status_on_summary))
.setTicker(context.getString(R.string.settings_status_on_summary))
.color = ContextCompat.getColor(context, R.color.primary_dark)
} else {
intent = Intent(Settings.ACTION_SETTINGS)
}
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
return builder.build()
}
}
}
+48 -40
Просмотреть файл
@@ -1,11 +1,11 @@
/*
* Copyright (C) 2013 The Android Open Source Project
* Copyright 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,50 +13,58 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import androidx.core.content.ContextCompat;
import android.util.SparseArray;
package org.traccar.client
public abstract class WakefulBroadcastReceiver extends BroadcastReceiver {
private static final String EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid";
private static final SparseArray<PowerManager.WakeLock> mActiveWakeLocks = new SparseArray<>();
private static int mNextId = 1;
import android.content.BroadcastReceiver
import android.content.Context
import android.util.SparseArray
import android.os.PowerManager.WakeLock
import android.content.Intent
import androidx.core.content.ContextCompat
import android.os.PowerManager
public static void startWakefulForegroundService(Context context, Intent intent) {
synchronized (mActiveWakeLocks) {
int id = mNextId;
mNextId++;
if (mNextId <= 0) {
mNextId = 1;
abstract class WakefulBroadcastReceiver : BroadcastReceiver() {
companion object {
private const val EXTRA_WAKE_LOCK_ID = "android.support.content.wakelockid"
private val activeWakeLocks = SparseArray<WakeLock>()
private var nextId = 1
fun startWakefulForegroundService(context: Context, intent: Intent) {
synchronized(activeWakeLocks) {
val id = nextId
nextId += 1
if (nextId <= 0) {
nextId = 1
}
intent.putExtra(EXTRA_WAKE_LOCK_ID, id)
ContextCompat.startForegroundService(context, intent)
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
WakefulBroadcastReceiver::class.java.simpleName
)
wakeLock.setReferenceCounted(false)
wakeLock.acquire((60 * 1000).toLong())
activeWakeLocks.put(id, wakeLock)
}
intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
ContextCompat.startForegroundService(context, intent);
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WakefulBroadcastReceiver.class.getSimpleName());
wl.setReferenceCounted(false);
wl.acquire(60*1000);
mActiveWakeLocks.put(id, wl);
}
}
public static boolean completeWakefulIntent(Intent intent) {
final int id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0);
if (id == 0) {
return false;
}
synchronized (mActiveWakeLocks) {
PowerManager.WakeLock wl = mActiveWakeLocks.get(id);
if (wl != null) {
wl.release();
mActiveWakeLocks.remove(id);
return true;
fun completeWakefulIntent(intent: Intent): Boolean {
val id = intent.getIntExtra(EXTRA_WAKE_LOCK_ID, 0)
if (id == 0) {
return false
}
synchronized(activeWakeLocks) {
val wakeLock = activeWakeLocks[id]
if (wakeLock != null) {
wakeLock.release()
activeWakeLocks.remove(id)
return true
}
return true
}
return true;
}
}
}
+7 -7
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2019 Anton Tananaev (anton@traccar.org)
* Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.traccar.client;
package org.traccar.client
import android.content.Context;
import android.content.Context
import org.traccar.client.PositionProvider.PositionListener
public class PositionProviderFactory {
object PositionProviderFactory {
public static PositionProvider create(Context context, PositionProvider.PositionListener listener) {
return new AndroidPositionProvider(context, listener);
fun create(context: Context, listener: PositionListener): PositionProvider {
return AndroidPositionProvider(context, listener)
}
}
+21 -29
Просмотреть файл
@@ -1,43 +1,35 @@
package org.traccar.client
package org.traccar.client;
import android.location.Location;
import android.os.Build;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Date;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@Config(sdk = Build.VERSION_CODES.P)
@RunWith(RobolectricTestRunner.class)
public class DatabaseHelperTest {
import android.location.Location
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@Config(sdk = [Build.VERSION_CODES.P])
@RunWith(RobolectricTestRunner::class)
class DatabaseHelperTest {
@Test
public void test() throws Exception {
fun test() {
DatabaseHelper databaseHelper = new DatabaseHelper(RuntimeEnvironment.application);
val databaseHelper = DatabaseHelper(ApplicationProvider.getApplicationContext())
Position position = new Position("123456789012345", new Location("gps"), 0);
position.setTime(new Date(0));
var position: Position? = Position("123456789012345", Location("gps"), 0.0)
assertNull(databaseHelper.selectPosition());
Assert.assertNull(databaseHelper.selectPosition())
databaseHelper.insertPosition(position);
databaseHelper.insertPosition(position!!)
position = databaseHelper.selectPosition();
position = databaseHelper.selectPosition()
assertNotNull(position);
Assert.assertNotNull(position)
databaseHelper.deletePosition(position.getId());
databaseHelper.deletePosition(position!!.id)
assertNull(databaseHelper.selectPosition());
Assert.assertNull(databaseHelper.selectPosition())
}
+25 -37
Просмотреть файл
@@ -1,49 +1,37 @@
package org.traccar.client
package org.traccar.client;
import android.location.Location
import android.os.Build
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.traccar.client.ProtocolFormatter.formatRequest
import android.location.Location;
import android.os.Build;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Date;
import static org.junit.Assert.assertEquals;
@Config(sdk = Build.VERSION_CODES.P)
@RunWith(RobolectricTestRunner.class)
public class ProtocolFormatterTest {
@Config(sdk = [Build.VERSION_CODES.P])
@RunWith(RobolectricTestRunner::class)
class ProtocolFormatterTest {
@Test
public void testFormatRequest() throws Exception {
Position position = new Position("123456789012345", new Location("gps"), 0);
position.setTime(new Date(0));
String url = ProtocolFormatter.formatRequest("http://localhost:5055", position);
assertEquals("http://localhost:5055?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0", url);
fun testFormatRequest() {
val position = Position("123456789012345", Location("gps"), 0.0)
val url = formatRequest("http://localhost:5055", position)
Assert.assertEquals("http://localhost:5055?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0", url)
}
@Test
public void testFormatPathPortRequest() throws Exception {
Position position = new Position("123456789012345", new Location("gps"), 0);
position.setTime(new Date(0));
String url = ProtocolFormatter.formatRequest("http://localhost:8888/path", position);
assertEquals("http://localhost:8888/path?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0", url);
fun testFormatPathPortRequest() {
val position = Position("123456789012345", Location("gps"), 0.0)
val url = formatRequest("http://localhost:8888/path", position)
Assert.assertEquals("http://localhost:8888/path?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0", url)
}
@Test
public void testFormatAlarmRequest() throws Exception {
Position position = new Position("123456789012345", new Location("gps"), 0);
position.setTime(new Date(0));
String url = ProtocolFormatter.formatRequest("http://localhost:5055/path", position, "alert message");
assertEquals("http://localhost:5055/path?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0&alarm=alert%20message", url);
fun testFormatAlarmRequest() {
val position = Position("123456789012345", Location("gps"), 0.0)
val url = formatRequest("http://localhost:5055/path", position, "alert message")
Assert.assertEquals("http://localhost:5055/path?id=123456789012345&timestamp=0&lat=0.0&lon=0.0&speed=0.0&bearing=0.0&altitude=0.0&accuracy=0.0&batt=0.0&alarm=alert%20message", url)
}
}
+14 -18
Просмотреть файл
@@ -1,26 +1,22 @@
package org.traccar.client
package org.traccar.client;
import android.os.Build
import org.junit.Assert
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.traccar.client.RequestManager.sendRequest
import android.os.Build;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertTrue;
@Config(sdk = Build.VERSION_CODES.P)
@RunWith(RobolectricTestRunner.class)
public class RequestManagerTest {
@Config(sdk = [Build.VERSION_CODES.P])
@RunWith(RobolectricTestRunner::class)
class RequestManagerTest {
@Ignore("Not a real unit test")
@Test
public void testSendRequest() throws Exception {
assertTrue(RequestManager.sendRequest("http://www.google.com"));
fun testSendRequest() {
Assert.assertTrue(sendRequest("http://www.google.com"))
}
}