Сравнить коммиты

...
32 Коммитов
Автор SHA1 Сообщение Дата
Anton Tananaev 43e108de8c Fix number format exception 2017-09-02 08:40:56 +12:00
Anton Tananaev fedce0e26b Fix number format exception 2017-09-02 08:38:42 +12:00
Anton Tananaev e24d725feb Disable failing unit test 2017-09-01 11:20:15 +12:00
Anton Tananaev 3198bee2f2 Use POST to send data (fix #279) 2017-09-01 11:18:26 +12:00
Anton Tananaev 92e0fb3ad5 Remove unnecessary permissions 2017-09-01 10:41:24 +12:00
Anton Tananaev f7b0df1c41 Update version code 2017-09-01 10:36:49 +12:00
Anton Tananaev 8a26c7e5ba Update localization files 2017-09-01 10:34:47 +12:00
Anton Tananaev 27c98b910b Remove redundant colors and use icons 2017-09-01 09:51:57 +12:00
Anton Tananaev 360958d250 New hidden app icon 2017-09-01 08:55:26 +12:00
Anton Tananaev 4d8d15d615 Implement static shortcuts 2017-08-31 14:16:04 +12:00
Anton Tananaev 7c49bb78f3 Implement proper shortcuts 2017-08-31 13:48:52 +12:00
Anton Tananaev e87c3c7783 Report location accuracy 2017-08-31 11:42:15 +12:00
Anton Tananaev 5428530dbd Add Firebase analytics 2017-08-31 11:07:16 +12:00
Anton Tananaev 3d5682d1b1 Move color to common file 2017-08-31 10:14:05 +12:00
Anton Tananaev 8f91c0e0ea New hidden app icon 2017-08-30 23:05:24 +12:00
Anton Tananaev 965feb9b04 Remove old launcher icons 2017-08-30 23:00:05 +12:00
Anton Tananaev 8f8a5e3f55 Add new app icon 2017-08-30 22:48:47 +12:00
Anton Tananaev 77154a81af Use some new Android APIs 2017-08-30 15:34:45 +12:00
Anton Tananaev 8d94b8d011 Implement notification channel 2017-08-30 15:27:00 +12:00
Anton Tananaev ab37fd478a Make spacing more standard 2017-08-30 13:51:06 +12:00
Anton Tananaev 79662bad35 Remove redundant preferences 2017-08-30 13:45:56 +12:00
Anton Tananaev 90373ff8fb Fix app unit tests 2017-08-30 13:44:40 +12:00
Anton Tananaev f2e7ae7c21 Finish app modernization 2017-08-30 13:42:04 +12:00
Anton Tananaev e0c5340d8b Modernize and fix compilation 2017-08-30 13:25:18 +12:00
Anton Tananaev 90fffa9873 Update built tools to latest 2017-08-30 12:59:06 +12:00
Anton Tananaev db51adaf86 Add github templates 2017-08-21 22:44:12 +12:00
Anton Tananaev 7eb6876491 Merge pull request #269 from BrianBlade/basepath
Add serverpath preference
2017-06-15 09:42:27 +12:00
BrianBlade 5db3407e88 remove check if URL isEmpty; dont set 5055 as default port 2017-06-14 14:36:00 +02:00
BrianBlade ff4fe90c6d simplify URL validation 2017-06-14 13:02:03 +02:00
BrianBlade 62a2599765 rename setServerUrl to validateServerUrl and add port validation 2017-06-14 11:00:56 +02:00
BrianBlade 149eee530a some requested fixes 2017-06-14 08:40:40 +02:00
BrianBlade 6e398d12fe add ServerURL preference (replaces address, port, encryption) 2017-06-13 12:40:32 +02:00
154 изменённых файлов: 1182 добавлений и 1388 удалений
+47
Просмотреть файл
@@ -0,0 +1,47 @@
# Contributing to Traccar
Please make sure you read this guide before commenting on any issue or creating a new issue or pull request.
Before asking anything, please search for an answer in:
- [Traccar documentation](https://www.traccar.org/documentation/)
- [Traccar forums](https://www.traccar.org/forums/)
- Relevant repositories (see below)
- [Google Search](https://www.google.com/)
GitHub issues should be used ONLY for feature requests, code discussions and bug reports. For general discussions please use [Traccar forums](https://www.traccar.org/forums/).
There are multiple Traccar projects. If you create a new issue you MUST do it in the relevant repository:
- [Traccar Server](https://github.com/tananaev/traccar/issues)
- [Traccar Web Interface](https://github.com/tananaev/traccar-web/issues)
- [Traccar Client for Android](https://github.com/tananaev/traccar-client-android/issues)
- [Traccar Client for iOS](https://github.com/tananaev/traccar-client-ios/issues)
- [Traccar Manager for Android](https://github.com/tananaev/traccar-manager-android/issues)
- [Traccar Manager for iOS](https://github.com/tananaev/traccar-manager-ios/issues)
If you are not sure where your issue belongs to, please use Traccar Server main repository.
If you want to discuss something that applies to both Android and iOS apps, please use Android repository.
## Bug Reports
Before creating a bug report make sure that you have tested latest official release with default configuration.
Only create a bug report issue if you are confident that there is a problem in Traccar software.
Provide as much details as possible, including log fragments, operating system and hardware information.
## Feature Requests
Before creating a feature request make sure that the feature or modification that you are requesting is not yet implemented.
Search reposiroty to ensure that there is no existing issues for your request. If there is, add a new comment on that issue.
Provide as much details as possible, including use case for your feature and any benefits that you can think of.
## Pull Requests
If you want to contribute some code to Traccar, it is recommended to discuss your solution with maintainers before starting any work.
Any code that you want to contribute must be of high quality and follow existing code patterns and styles.
+7
Просмотреть файл
@@ -0,0 +1,7 @@
<!--
Do you want to ask a question? Traccar official forum is the best place for getting support:
https://www.traccar.org/forums/
-->
+1
Просмотреть файл
@@ -4,3 +4,4 @@ local.properties
*.iml
build/
.DS_Store
google-services.json
+29 -11
Просмотреть файл
@@ -1,14 +1,16 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileSdkVersion 26
buildToolsVersion '26.0.1'
defaultConfig {
minSdkVersion 3
targetSdkVersion 25
versionCode 36
versionName '4.3'
applicationId "org.traccar.client"
buildConfigField "boolean", "HIDDEN_APP", "false"
minSdkVersion 14
targetSdkVersion 26
versionCode 38
versionName '5.1'
}
lintOptions {
@@ -16,10 +18,8 @@ android {
}
productFlavors {
regular {
applicationId "org.traccar.client"
buildConfigField "boolean", "HIDDEN_APP", "false"
}
regular
google
hidden {
applicationId "org.traccar.client.hidden"
buildConfigField "boolean", "HIDDEN_APP", "true"
@@ -35,6 +35,24 @@ android {
}
dependencies {
compile 'com.android.support:design:26.0.1'
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.robolectric:robolectric:3.4.2'
googleCompile 'com.google.firebase:firebase-core:11.2.0'
googleCompile 'com.google.firebase:firebase-crash:11.2.0'
}
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Google")) {
apply plugin: 'com.google.gms.google-services'
task copyJson(type: Copy) {
from '../../traccar-shared/google-services'
into '.'
include 'traccar-client-android.json'
rename('traccar-client-android.json', 'google-services.json')
}
afterEvaluate {
preBuild.dependsOn copyJson
}
}
+12
Просмотреть файл
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="org.traccar.client.GoogleMainApplication"
tools:replace="android:name">
</application>
</manifest>
+30
Просмотреть файл
@@ -0,0 +1,30 @@
/*
* Copyright 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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 com.google.firebase.analytics.FirebaseAnalytics;
public class GoogleMainApplication extends MainApplication {
private FirebaseAnalytics firebaseAnalytics;
@Override
public void onCreate() {
super.onCreate();
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
}
}
+1 -1
Просмотреть файл
@@ -6,7 +6,7 @@
<application
android:label="@string/hidden_app_name"
tools:replace="label">
tools:replace="android:label">
<receiver android:name="org.traccar.client.DialLaunchReceiver" >
<intent-filter>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.5 KiB

После

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.3 KiB

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.2 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.0 KiB

После

Ширина:  |  Высота:  |  Размер: 4.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 14 KiB

После

Ширина:  |  Высота:  |  Размер: 7.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.7 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 20 KiB

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.4 KiB

+12 -4
Просмотреть файл
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.traccar.client">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
@@ -14,10 +15,12 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/TraccarTheme"
android:theme="@style/AppTheme"
android:name=".MainApplication">
<activity android:name=".MainActivity" android:launchMode="singleTask" />
<activity
android:name=".MainActivity"
android:launchMode="singleTask" />
<activity-alias
android:name=".Launcher"
@@ -26,6 +29,9 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity-alias>
<activity android:name=".StatusActivity"/>
@@ -33,9 +39,11 @@
<activity android:name=".AboutActivity"/>
<activity
android:name=".ShortcutActivity">
android:name=".ShortcutActivity"
android:label="@string/menu_shortcuts">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter>
</activity>
Двоичные данные
Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 48 KiB

-134
Просмотреть файл
@@ -1,134 +0,0 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 android.support.v4.content;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;
import android.util.SparseArray;
/**
* Helper for the common pattern of implementing a {@link BroadcastReceiver}
* that receives a device wakeup event and then passes the work off
* to a {@link android.app.Service}, while ensuring that the
* device does not go back to sleep during the transition.
*
* <p>This class takes care of creating and managing a partial wake lock
* for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
* permission to use it.</p>
*
* <h3>Example</h3>
*
* <p>A {@link WakefulBroadcastReceiver} uses the method
* {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
* to start the service that does the work. This method is comparable to
* {@link android.content.Context#startService startService()}, except that
* the {@link WakefulBroadcastReceiver} is holding a wake lock when the service
* starts. The intent that is passed with
* {@link WakefulBroadcastReceiver#startWakefulService startWakefulService()}
* holds an extra identifying the wake lock.</p>
*
* {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulReceiver.java complete}
*
* <p>The service (in this example, an {@link android.app.IntentService}) does
* some work. When it is finished, it releases the wake lock by calling
* {@link WakefulBroadcastReceiver#completeWakefulIntent
* completeWakefulIntent(intent)}. The intent it passes as a parameter
* is the same intent that the {@link WakefulBroadcastReceiver} originally
* passed in.</p>
*
* {@sample development/samples/Support4Demos/src/com/example/android/supportv4/content/SimpleWakefulService.java complete}
*/
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<PowerManager.WakeLock>();
private static int mNextId = 1;
/**
* Do a {@link android.content.Context#startService(android.content.Intent)
* Context.startService}, but holding a wake lock while the service starts.
* This will modify the Intent to hold an extra identifying the wake lock;
* when the service receives it in {@link android.app.Service#onStartCommand
* Service.onStartCommand}, it should pass back the Intent it receives there to
* {@link #completeWakefulIntent(android.content.Intent)} in order to release
* the wake lock.
*
* @param context The Context in which it operate.
* @param intent The Intent with which to start the service, as per
* {@link android.content.Context#startService(android.content.Intent)
* Context.startService}.
*/
public static ComponentName startWakefulService(Context context, Intent intent) {
synchronized (mActiveWakeLocks) {
int id = mNextId;
mNextId++;
if (mNextId <= 0) {
mNextId = 1;
}
intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
ComponentName comp = context.startService(intent);
if (comp == null) {
return null;
}
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"wake:" + comp.flattenToShortString());
wl.setReferenceCounted(false);
wl.acquire(60*1000);
mActiveWakeLocks.put(id, wl);
return comp;
}
}
/**
* Finish the execution from a previous {@link #startWakefulService}. Any wake lock
* that was being held will now be released.
*
* @param intent The Intent as originally generated by {@link #startWakefulService}.
* @return Returns true if the intent is associated with a wake lock that is
* now released; returns false if there was no wake lock specified for it.
*/
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;
}
// We return true whether or not we actually found the wake lock
// the return code is defined to indicate whether the Intent contained
// an identifier for a wake lock that it was supposed to match.
// We just log a warning here if there is no wake lock found, which could
// happen for example if this function is called twice on the same
// intent or the process is killed and restarted before processing the intent.
Log.w("WakefulBroadcastReceive", "No active wake lock id #" + id);
return true;
}
}
}
+6 -4
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,22 +15,24 @@
*/
package org.traccar.client;
import android.app.Activity;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
public class AboutActivity extends Activity {
public class AboutActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
TextView title = (TextView) findViewById(R.id.title);
TextView title = findViewById(R.id.title);
try {
title.setText(title.getText() + " " + getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName);
} catch (NameNotFoundException e) {
Log.w(AboutActivity.class.getSimpleName(), e);
}
}
+1 -1
Просмотреть файл
@@ -26,7 +26,7 @@ public class AutostartReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences.getBoolean(MainActivity.KEY_STATUS, false)) {
if (sharedPreferences.getBoolean(MainFragment.KEY_STATUS, false)) {
startWakefulService(context, new Intent(context, TrackingService.class));
}
}
+4 -1
Просмотреть файл
@@ -27,7 +27,7 @@ import java.util.Date;
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final int DATABASE_VERSION = 2;
public static final String DATABASE_NAME = "traccar.db";
public interface DatabaseHandler<T> {
@@ -79,6 +79,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
"altitude REAL," +
"speed REAL," +
"course REAL," +
"accuracy REAL," +
"battery REAL)");
}
@@ -97,6 +98,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
values.put("altitude", position.getAltitude());
values.put("speed", position.getSpeed());
values.put("course", position.getCourse());
values.put("accuracy", position.getCourse());
values.put("battery", position.getBattery());
db.insertOrThrow("position", null, values);
@@ -129,6 +131,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
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")));
} else {
+7 -285
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,295 +15,17 @@
*/
package org.traccar.client;
import android.Manifest;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.TwoStatePreference;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
@SuppressWarnings("deprecation")
public class MainActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
private static final String TAG = MainActivity.class.getSimpleName();
public static final String KEY_DEVICE = "id";
public static final String KEY_ADDRESS = "address";
public static final String KEY_PORT = "port";
public static final String KEY_SECURE = "secure";
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_PROVIDER = "provider";
public static final String KEY_STATUS = "status";
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
private SharedPreferences sharedPreferences;
private AlarmManager alarmManager;
private PendingIntent alarmIntent;
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (BuildConfig.HIDDEN_APP) {
removeLauncherIcon();
}
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
addPreferencesFromResource(R.xml.preferences);
initPreferences();
findPreference(KEY_DEVICE).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return newValue != null && !newValue.equals("");
}
});
findPreference(KEY_ADDRESS).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return newValue != null && Patterns.DOMAIN_NAME.matcher((String) newValue).matches();
} else {
return newValue != null && !((String) newValue).isEmpty();
}
}
});
findPreference(KEY_PORT).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 && value <= 65536;
} catch (NumberFormatException e) {
Log.w(TAG, e);
}
}
return 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) getSystemService(ALARM_SERVICE);
alarmIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, AutostartReceiver.class), 0);
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
}
}
private void removeLauncherIcon() {
String className = MainActivity.class.getCanonicalName().replace(".MainActivity", ".Launcher");
ComponentName componentName = new ComponentName(getPackageName(), className);
PackageManager packageManager = getPackageManager();
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(this);
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.hidden_alert));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
private void addShortcuts(String action, int name) {
Intent shortcutIntent = new Intent(Intent.ACTION_MAIN);
shortcutIntent.setComponent(new ComponentName(getPackageName(), ShortcutActivity.class.getCanonicalName()));
shortcutIntent.putExtra(ShortcutActivity.EXTRA_ACTION, action);
Intent installShortCutIntent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
installShortCutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
installShortCutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(name));
installShortCutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher));
sendBroadcast(installShortCutIntent);
}
private boolean hasPermission(String permission) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
return true;
}
return checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
@Override
protected void onResume() {
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPause() {
super.onPause();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
}
private void setPreferencesEnabled(boolean enabled) {
findPreference(KEY_DEVICE).setEnabled(enabled);
findPreference(KEY_ADDRESS).setEnabled(enabled);
findPreference(KEY_PORT).setEnabled(enabled);
findPreference(KEY_SECURE).setEnabled(enabled);
findPreference(KEY_INTERVAL).setEnabled(enabled);
findPreference(KEY_DISTANCE).setEnabled(enabled);
findPreference(KEY_ANGLE).setEnabled(enabled);
findPreference(KEY_PROVIDER).setEnabled(enabled);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KEY_STATUS)) {
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
} else {
stopTrackingService();
}
} else if (key.equals(KEY_DEVICE)) {
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.status) {
startActivity(new Intent(this, StatusActivity.class));
return true;
} else if (item.getItemId() == R.id.shortcuts) {
addShortcuts(ShortcutActivity.EXTRA_ACTION_START, R.string.shortcut_start);
addShortcuts(ShortcutActivity.EXTRA_ACTION_STOP, R.string.shortcut_stop);
addShortcuts(ShortcutActivity.EXTRA_ACTION_SOS, R.string.shortcut_sos);
return true;
} else if (item.getItemId() == R.id.about) {
startActivity(new Intent(this, AboutActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
private void initPreferences() {
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
if (!sharedPreferences.contains(KEY_DEVICE)) {
String id = String.valueOf(new Random().nextInt(900000) + 100000);
sharedPreferences.edit().putString(KEY_DEVICE, id).commit();
((EditTextPreference) findPreference(KEY_DEVICE)).setText(id);
}
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
}
private void startTrackingService(boolean checkPermission, boolean permission) {
if (checkPermission) {
Set<String> missingPermissions = new HashSet<>();
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
missingPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
missingPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (missingPermissions.isEmpty()) {
permission = true;
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(missingPermissions.toArray(new String[missingPermissions.size()]),
PERMISSIONS_REQUEST_LOCATION);
}
return;
}
}
if (permission) {
setPreferencesEnabled(false);
startService(new Intent(this, TrackingService.class));
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
15000, 15000, alarmIntent);
} else {
sharedPreferences.edit().putBoolean(KEY_STATUS, false).commit();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TwoStatePreference preference = (TwoStatePreference) findPreference(KEY_STATUS);
preference.setChecked(false);
} else {
CheckBoxPreference preference = (CheckBoxPreference) findPreference(KEY_STATUS);
preference.setChecked(false);
}
}
}
private void stopTrackingService() {
alarmManager.cancel(alarmIntent);
stopService(new Intent(this, TrackingService.class));
setPreferencesEnabled(true);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
boolean granted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
granted = false;
break;
}
}
startTrackingService(false, granted);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().replace(android.R.id.content, new MainFragment()).commit();
}
}
+23 -1
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2016 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,14 +15,36 @@
*/
package org.traccar.client;
import android.annotation.TargetApi;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
public class MainApplication extends Application {
public static final String PRIMARY_CHANNEL = "default";
@Override
public void onCreate() {
super.onCreate();
System.setProperty("http.keepAliveDuration", String.valueOf(30 * 60 * 1000));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
registerChannel();
}
}
@TargetApi(Build.VERSION_CODES.O)
private void registerChannel() {
NotificationChannel channel = new NotificationChannel(
PRIMARY_CHANNEL, getString(R.string.channel_default), NotificationManager.IMPORTANCE_MIN);
channel.setLightColor(Color.GREEN);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
}
}
+311
Просмотреть файл
@@ -0,0 +1,311 @@
/*
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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.Manifest;
import android.app.AlarmManager;
import android.app.AlertDialog;
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.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.TwoStatePreference;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.webkit.URLUtil;
import android.widget.Toast;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class MainFragment extends PreferenceFragment 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_PROVIDER = "provider";
public static final String KEY_STATUS = "status";
private static final int PERMISSIONS_REQUEST_LOCATION = 2;
private SharedPreferences sharedPreferences;
private AlarmManager alarmManager;
private PendingIntent alarmIntent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (BuildConfig.HIDDEN_APP) {
removeLauncherIcon();
}
setHasOptionsMenu(true);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
migrateLegacyPreferences(sharedPreferences);
addPreferencesFromResource(R.xml.preferences);
initPreferences();
findPreference(KEY_DEVICE).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return newValue != null && !newValue.equals("");
}
});
findPreference(KEY_URL).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
return (newValue != null) && validateServerURL(newValue.toString());
}
});
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);
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
}
}
private void removeLauncherIcon() {
String className = MainActivity.class.getCanonicalName().replace(".MainActivity", ".Launcher");
ComponentName componentName = new ComponentName(getActivity().getPackageName(), className);
PackageManager packageManager = getActivity().getPackageManager();
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();
}
}
private boolean hasPermission(String permission) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
return true;
}
return ContextCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED;
}
@Override
public void onResume() {
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void 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_PROVIDER).setEnabled(enabled);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(KEY_STATUS)) {
if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
startTrackingService(true, false);
} else {
stopTrackingService();
}
} else if (key.equals(KEY_DEVICE)) {
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
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;
} else if (item.getItemId() == R.id.about) {
startActivity(new Intent(getActivity(), AboutActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
private void initPreferences() {
PreferenceManager.setDefaultValues(getActivity(), 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);
}
findPreference(KEY_DEVICE).setSummary(sharedPreferences.getString(KEY_DEVICE, null));
}
private void startTrackingService(boolean checkPermission, boolean permission) {
if (checkPermission) {
Set<String> missingPermissions = new HashSet<>();
if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
missingPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (!hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) {
missingPermissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (missingPermissions.isEmpty()) {
permission = true;
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(missingPermissions.toArray(new String[missingPermissions.size()]),
PERMISSIONS_REQUEST_LOCATION);
}
return;
}
}
if (permission) {
setPreferencesEnabled(false);
ContextCompat.startForegroundService(getActivity(), new Intent(getActivity(), TrackingService.class));
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
ALARM_MANAGER_INTERVAL, ALARM_MANAGER_INTERVAL, alarmIntent);
} else {
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TwoStatePreference preference = (TwoStatePreference) findPreference(KEY_STATUS);
preference.setChecked(false);
} else {
CheckBoxPreference preference = (CheckBoxPreference) findPreference(KEY_STATUS);
preference.setChecked(false);
}
}
}
private void stopTrackingService() {
alarmManager.cancel(alarmIntent);
getActivity().stopService(new Intent(getActivity(), TrackingService.class));
setPreferencesEnabled(true);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
boolean granted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
granted = false;
break;
}
}
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;
}
Toast.makeText(getActivity(), R.string.error_msg_invalid_url, Toast.LENGTH_LONG).show();
return false;
}
private void migrateLegacyPreferences(SharedPreferences preferences) {
String port = preferences.getString("port", null);
if (port != null) {
Log.d(TAG, "Migrating to URL preference");
String host = preferences.getString("address", getString(R.string.settings_url_default_value));
String scheme = preferences.getBoolean("secure", false) ? "https" : "http";
Uri.Builder builder = new Uri.Builder();
builder.scheme(scheme).encodedAuthority(host + ":" + port).build();
SharedPreferences.Editor editor = preferences.edit();
editor.putString(KEY_URL, builder.toString());
editor.remove("port");
editor.remove("address");
editor.remove("secure");
editor.apply();
}
}
}
-58
Просмотреть файл
@@ -1,58 +0,0 @@
/*
* Copyright 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* 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 java.util.regex.Pattern;
public class Patterns {
private static final String IP_ADDRESS_STRING =
"((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ "|[1-9][0-9]|[0-9]))";
private static final String UCS_CHAR = "[" +
"\u00A0-\uD7FF" +
"\uF900-\uFDCF" +
"\uFDF0-\uFFEF" +
"\uD800\uDC00-\uD83F\uDFFD" +
"\uD840\uDC00-\uD87F\uDFFD" +
"\uD880\uDC00-\uD8BF\uDFFD" +
"\uD8C0\uDC00-\uD8FF\uDFFD" +
"\uD900\uDC00-\uD93F\uDFFD" +
"\uD940\uDC00-\uD97F\uDFFD" +
"\uD980\uDC00-\uD9BF\uDFFD" +
"\uD9C0\uDC00-\uD9FF\uDFFD" +
"\uDA00\uDC00-\uDA3F\uDFFD" +
"\uDA40\uDC00-\uDA7F\uDFFD" +
"\uDA80\uDC00-\uDABF\uDFFD" +
"\uDAC0\uDC00-\uDAFF\uDFFD" +
"\uDB00\uDC00-\uDB3F\uDFFD" +
"\uDB44\uDC00-\uDB7F\uDFFD" +
"&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]";
private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR;
private static final String IRI_LABEL =
"[" + LABEL_CHAR + "](?:[" + LABEL_CHAR + "\\-]{0,61}[" + LABEL_CHAR + "]){0,1}";
private static final String RELAXED_DOMAIN_NAME =
"(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS_STRING + ")";
public static final Pattern DOMAIN_NAME = Pattern.compile(RELAXED_DOMAIN_NAME);
}
+87 -19
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2015 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.traccar.client;
import android.location.Location;
import android.location.LocationManager;
import java.util.Date;
@@ -32,43 +33,110 @@ public class Position {
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;
}
private long id;
public long getId() { return id; }
public void setId(long id) { this.id = 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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
public double getBattery() {
return battery;
}
public void setBattery(double battery) {
this.battery = battery;
}
}
+9 -16
Просмотреть файл
@@ -15,7 +15,6 @@
*/
package org.traccar.client;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -23,7 +22,6 @@ import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -58,10 +56,10 @@ public abstract class PositionProvider {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
deviceId = preferences.getString(MainActivity.KEY_DEVICE, null);
interval = Long.parseLong(preferences.getString(MainActivity.KEY_INTERVAL, null)) * 1000;
distance = Integer.parseInt(preferences.getString(MainActivity.KEY_DISTANCE, null));
angle = Integer.parseInt(preferences.getString(MainActivity.KEY_ANGLE, null));
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"));
if (distance > 0 || angle > 0) {
requestInterval = MINIMUM_INTERVAL;
@@ -69,7 +67,7 @@ public abstract class PositionProvider {
requestInterval = interval;
}
type = preferences.getString(MainActivity.KEY_PROVIDER, "gps");
type = preferences.getString(MainFragment.KEY_PROVIDER, "gps");
}
public abstract void startUpdates();
@@ -89,16 +87,11 @@ public abstract class PositionProvider {
}
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
public static double getBatteryLevel(Context context) {
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.ECLAIR) {
Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
return (level * 100.0) / scale;
} else {
return 0;
}
Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
return (level * 100.0) / scale;
}
}
+6 -7
Просмотреть файл
@@ -19,14 +19,13 @@ import android.net.Uri;
public class ProtocolFormatter {
public static String formatRequest(String address, int port, boolean secure, Position position) {
return formatRequest(address, port, secure, position, null);
public static String formatRequest(String url, Position position) {
return formatRequest(url, position, null);
}
public static String formatRequest(String address, int port, boolean secure, Position position, String alarm) {
Uri.Builder builder = new Uri.Builder();
builder.scheme(secure ? "https" : "http").encodedAuthority(address + ':' + port)
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()))
@@ -34,6 +33,7 @@ public class ProtocolFormatter {
.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 (alarm != null) {
@@ -42,5 +42,4 @@ public class ProtocolFormatter {
return builder.build().toString();
}
}
+3 -1
Просмотреть файл
@@ -16,6 +16,7 @@
package org.traccar.client;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
@@ -56,6 +57,7 @@ public class RequestManager {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(TIMEOUT);
connection.setConnectTimeout(TIMEOUT);
connection.setRequestMethod("POST");
connection.connect();
inputStream = connection.getInputStream();
while (inputStream.read() != -1);
@@ -68,7 +70,7 @@ public class RequestManager {
inputStream.close();
}
} catch (IOException secondError) {
return false;
Log.w(RequestManager.class.getSimpleName(), secondError);
}
}
}
+72 -21
Просмотреть файл
@@ -15,7 +15,6 @@
*/
package org.traccar.client;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -23,27 +22,81 @@ import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.pm.ShortcutInfoCompat;
import android.support.v4.content.pm.ShortcutManagerCompat;
import android.support.v4.graphics.drawable.IconCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class ShortcutActivity extends Activity {
public class ShortcutActivity extends AppCompatActivity {
public static final String EXTRA_ACTION = "action";
public static final String EXTRA_ACTION_START = "start";
public static final String EXTRA_ACTION_STOP = "stop";
public static final String EXTRA_ACTION_SOS = "sos";
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);
checkShortcutAction(getIntent());
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();
}
});
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
checkShortcutAction(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));
}
@SuppressWarnings("MissingPermission")
@@ -60,14 +113,11 @@ public class ShortcutActivity extends Activity {
if (location != null) {
Position position = new Position(
preferences.getString(MainActivity.KEY_DEVICE, null),
preferences.getString(MainFragment.KEY_DEVICE, null),
location, PositionProvider.getBatteryLevel(this));
String request = ProtocolFormatter.formatRequest(
preferences.getString(MainActivity.KEY_ADDRESS, null),
Integer.parseInt(preferences.getString(MainActivity.KEY_PORT, null)),
preferences.getBoolean(MainActivity.KEY_SECURE, false),
position, ALARM_SOS);
preferences.getString(MainFragment.KEY_URL, null), position, ALARM_SOS);
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
@Override
@@ -85,34 +135,35 @@ public class ShortcutActivity extends Activity {
}
}
private void checkShortcutAction(Intent intent) {
private boolean executeAction(Intent intent) {
String action;
if (intent.hasExtra("shortcutAction")) {
action = intent.getBooleanExtra("shortcutAction", false)
? EXTRA_ACTION_START : EXTRA_ACTION_STOP;
? ACTION_START : ACTION_STOP;
} else {
action = intent.getStringExtra(EXTRA_ACTION);
}
if (action != null) {
switch (action) {
case EXTRA_ACTION_START:
case ACTION_START:
PreferenceManager.getDefaultSharedPreferences(this)
.edit().putBoolean(MainActivity.KEY_STATUS, true).commit();
startService(new Intent(this, TrackingService.class));
.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 EXTRA_ACTION_STOP:
case ACTION_STOP:
PreferenceManager.getDefaultSharedPreferences(this)
.edit().putBoolean(MainActivity.KEY_STATUS, false).commit();
.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 EXTRA_ACTION_SOS:
case ACTION_SOS:
sendAlarm();
break;
}
finish();
}
finish();
return action != null;
}
}
+10 -8
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2013 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,19 +21,20 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import android.app.ListActivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class StatusActivity extends ListActivity {
public class StatusActivity extends AppCompatActivity {
private static final int LIMIT = 20;
private static final LinkedList<String> messages = new LinkedList<String>();
private static final Set<ArrayAdapter<String>> adapters = new HashSet<ArrayAdapter<String>>();
private static final LinkedList<String> messages = new LinkedList<>();
private static final Set<ArrayAdapter<String>> adapters = new HashSet<>();
private static void notifyAdapters() {
for (ArrayAdapter<String> adapter : adapters) {
@@ -61,9 +62,10 @@ public class StatusActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.status);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, messages);
setListAdapter(adapter);
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);
}
+6 -15
Просмотреть файл
@@ -17,7 +17,6 @@ package org.traccar.client;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.preference.PreferenceManager;
@@ -36,9 +35,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
private Handler handler;
private SharedPreferences preferences;
private String address;
private int port;
private boolean secure;
private String url;
private PositionProvider positionProvider;
private DatabaseHelper databaseHelper;
@@ -47,11 +44,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
private PowerManager.WakeLock wakeLock;
private void lock() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
wakeLock.acquire();
} else {
wakeLock.acquire(WAKE_LOCK_TIMEOUT);
}
wakeLock.acquire(WAKE_LOCK_TIMEOUT);
}
private void unlock() {
@@ -64,7 +57,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
this.context = context;
handler = new Handler();
preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences.getString(MainActivity.KEY_PROVIDER, "gps").equals("mixed")) {
if (preferences.getString(MainFragment.KEY_PROVIDER, "gps").equals("mixed")) {
positionProvider = new MixedPositionProvider(context, this);
} else {
positionProvider = new SimplePositionProvider(context, this);
@@ -73,9 +66,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
networkManager = new NetworkManager(context, this);
isOnline = networkManager.isOnline();
address = preferences.getString(MainActivity.KEY_ADDRESS, null);
port = Integer.parseInt(preferences.getString(MainActivity.KEY_PORT, null));
secure = preferences.getBoolean(MainActivity.KEY_SECURE, false);
url = preferences.getString(MainFragment.KEY_URL, null);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
@@ -164,7 +155,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
public void onComplete(boolean success, Position result) {
if (success) {
if (result != null) {
if (result.getDeviceId().equals(preferences.getString(MainActivity.KEY_DEVICE, null))) {
if (result.getDeviceId().equals(preferences.getString(MainFragment.KEY_DEVICE, null))) {
send(result);
} else {
delete(result);
@@ -199,7 +190,7 @@ public class TrackingController implements PositionProvider.PositionListener, Ne
private void send(final Position position) {
log("send", position);
lock();
String request = ProtocolFormatter.formatRequest(address, port, secure, position);
String request = ProtocolFormatter.formatRequest(url, position);
RequestManager.sendRequestAsync(request, new RequestManager.RequestHandler() {
@Override
public void onComplete(boolean success) {
+11 -43
Просмотреть файл
@@ -1,5 +1,5 @@
/*
* Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
* Copyright 2012 - 2017 Anton Tananaev (anton.tananaev@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,11 +23,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TrackingService extends Service {
private static final String TAG = TrackingService.class.getSimpleName();
@@ -35,35 +34,14 @@ public class TrackingService extends Service {
private TrackingController trackingController;
@SuppressWarnings("deprecation")
private static Notification createNotification(Context context) {
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return new Notification.Builder(context)
return new NotificationCompat.Builder(context, MainApplication.PRIMARY_CHANNEL)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(context.getString(R.string.settings_status_on_summary))
.setContentIntent(pendingIntent)
.setPriority(Notification.PRIORITY_MIN)
.setPriority(NotificationCompat.PRIORITY_MIN)
.build();
} else {
Notification notification = new Notification(android.R.drawable.stat_notify_sync_noanim, null, 0);
try {
Method method = notification.getClass().getMethod("setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class);
try {
method.invoke(notification, context, context.getString(R.string.app_name), context.getString(R.string.settings_status_on_summary), pendingIntent);
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
Log.w(TAG, e);
}
} catch (SecurityException | NoSuchMethodException e) {
Log.w(TAG, e);
}
return notification;
}
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
@@ -94,10 +72,8 @@ public class TrackingService extends Service {
trackingController = new TrackingController(this);
trackingController.start();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
startForeground(NOTIFICATION_ID, createNotification(this));
startService(new Intent(this, HideNotificationService.class));
}
startForeground(NOTIFICATION_ID, createNotification(this));
ContextCompat.startForegroundService(this, new Intent(this, HideNotificationService.class));
}
@Override
@@ -105,18 +81,12 @@ public class TrackingService extends Service {
return null;
}
@SuppressWarnings("deprecation")
@Override
public void onStart(Intent intent, int startId) {
if (intent != null) {
AutostartReceiver.completeWakefulIntent(intent);
}
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
if (intent != null) {
AutostartReceiver.completeWakefulIntent(intent);
}
return START_STICKY;
}
@@ -125,9 +95,7 @@ public class TrackingService extends Service {
Log.i(TAG, "service destroy");
StatusActivity.addMessage(getString(R.string.status_service_destroy));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
stopForeground(true);
}
stopForeground(true);
if (trackingController != null) {
trackingController.stop();
Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 3.6 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 4.8 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 7.2 KiB

+29 -48
Просмотреть файл
@@ -1,56 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
android:layout_height="fill_parent"
android:layout_toRightOf="@+id/logo"
android:layout_toEndOf="@+id/logo"
android:padding="@dimen/spacing_normal"
android:orientation="vertical" >
<ImageView
android:id="@+id/logo"
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/about_margin"
android:layout_marginStart="@dimen/about_margin"
android:layout_marginTop="@dimen/about_margin"
android:src="@drawable/logo"
android:contentDescription="@string/app_logo" />
android:layout_marginBottom="@dimen/spacing_normal"
android:text="@string/app_name"
android:textAppearance="?android:attr/textAppearanceLarge" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="@dimen/about_margin"
android:layout_toRightOf="@+id/logo"
android:layout_toEndOf="@+id/logo"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_normal"
android:text="@string/about_description" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/about_spacing"
android:text="@string/app_name"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/about_spacing"
android:text="@string/about_description" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/about_spacing"
android:text="@string/about_license" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/about_spacing"
android:text="@string/about_web"
android:autoLink="web" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_normal"
android:text="@string/about_license" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_web"
android:autoLink="web" />
</LinearLayout>
+6
Просмотреть файл
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-5
Просмотреть файл
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
+5 -8
Просмотреть файл
@@ -1,19 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/status"
android:title="@string/menu_status"
android:showAsAction="always" />
<item
android:id="@+id/shortcuts"
android:title="@string/menu_shortcuts"
android:showAsAction="never" />
app:showAsAction="ifRoom" />
<item
android:id="@+id/about"
android:title="@string/menu_about"
android:showAsAction="never" />
app:showAsAction="never" />
</menu>
+4 -2
Просмотреть файл
@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/clear"
android:title="@string/menu_clear"
android:showAsAction="always" />
app:showAsAction="always" />
</menu>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/primary"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/primary"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_sos_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_sos_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_start_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_start_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_stop_foreground"/>
</adaptive-icon>
+5
Просмотреть файл
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/white"/>
<foreground android:drawable="@mipmap/ic_stop_foreground"/>
</adaptive-icon>
Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 930 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 943 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.9 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.1 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 9.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.0 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 13 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.8 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.4 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.2 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 11 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.4 KiB

+9 -8
Просмотреть файл
@@ -2,14 +2,15 @@
<resources>
<string name="app_name">عميل Traccar</string>
<string name="app_logo">Traccar</string>
<string name="shortcut_start">Traccar - بدء</string>
<string name="shortcut_stop">Traccar - إيقاف</string>
<string name="settings_id_title">معرف الجهاز</string>
<string name="settings_address_title">عنوان الخادم</string>
<string name="settings_address_summary">اسم النطاق أو عنوان بروتوكول الإنترنت</string>
<string name="settings_port_title">منفذ الخادم</string>
<string name="settings_port_summary">تعقب منفذ خادم تي سي بي</string>
<string name="settings_url_title">رابط الخادم</string>
<string name="settings_url_summary">رابط خادم التتبع</string>
<string name="settings_interval_title">تردد</string>
<string name="settings_interval_summary">فترة التقرير بالثواني</string>
<string name="settings_distance_title">المسافة</string>
<string name="settings_distance_summary">مسافة التقرير بالميتر</string>
<string name="settings_angle_title">الزاوية</string>
<string name="settings_angle_summary">زاوية التقرير بالدرجات</string>
<string name="settings_status_title">حالة الخدمة</string>
<string name="settings_status_off">بدء</string>
<string name="settings_status_on">إيقاف</string>
@@ -29,11 +30,11 @@
<string name="about_description">محدد الموقع في الوقت الفعلي لأجهزة Android. متوافق مع خادم Traccar ونظم تعقب أخرى </string>
<string name="about_license">هذا التطبيق مجاني ومفتوح المصدر، الكود المصدري مرخص تحت رخصة Apache الإصدار 2.0 و متاح على GitHub.</string>
<string name="about_web">لمزيد من المعلومات زر \nwww.traccar.org/client</string>
<string name="status_service_create">تم إنشاء الخدمة</string>
<string name="status_service_destroy">تم تدمير الخدمة</string>
<string name="status_send_success">تم الإرسال بنجاح</string>
<string name="status_send_fail">فشل الإرسال</string>
<string name="status_location_update">تحديث الموقع</string>
<string name="status_connectivity_change">تغير الارتباط</string>
<string name="hidden_app_name">إعدادات الجهاز</string>
<string name="hidden_alert">تم إخفاء التطبيق. لفتحه مجددا رجاءا اتصل بـ8722227 (TRACCAR).</string>
<string name="error_msg_invalid_url">أرجو إدخال رابط صحيح http:// أو https://</string>
</resources>
-14
Просмотреть файл
@@ -2,14 +2,7 @@
<resources>
<string name="app_name">Traccar клиент</string>
<string name="app_logo">Traccar</string>
<string name="shortcut_start">Traccar -Старт</string>
<string name="shortcut_stop">Traccar - Стоп</string>
<string name="shortcut_sos">Traccar SOS</string>
<string name="settings_id_title">Идентификатор</string>
<string name="settings_address_title">Адрес на сървъра</string>
<string name="settings_address_summary">Домейн или IP адрес</string>
<string name="settings_port_title">Порт на сървъра</string>
<string name="settings_port_summary">ТСР порт на сървъра</string>
<string name="settings_interval_title">Честота</string>
<string name="settings_interval_summary">Докладван интервал в сек.</string>
<string name="settings_distance_title">Разстояние</string>
@@ -21,11 +14,6 @@
<string name="settings_status_on">Стоп</string>
<string name="settings_status_off_summary">Услугата е спряна</string>
<string name="settings_status_on_summary">Услугата работи</string>
<string name="settings_secure_title">Шифроване</string>
<string name="settings_secure_off">Активиране</string>
<string name="settings_secure_on">Деактивиране</string>
<string name="settings_secure_off_summary">Шифроване изкл.</string>
<string name="settings_secure_on_summary">Шифроване вкл.</string>
<string name="settings_provider_title">Доставчик на местоположение</string>
<string name="settings_provider_summary">Източник на данни за местоположението</string>
<string name="settings_provider_gps">GPS</string>
@@ -40,8 +28,6 @@
<string name="about_description">Real time GPS tracker for Android devices. Compatible with Traccar Server and other tracking systems.</string>
<string name="about_license">This application is free and open source, source code is licensed under Apache License Version 2.0 and available on GitHub.</string>
<string name="about_web">For more information visit\nwww.traccar.org/client</string>
<string name="status_service_create">Услугата е създадена</string>
<string name="status_service_destroy">Услугата е премахната</string>
<string name="status_send_success">Успешно изпращане</string>
<string name="status_send_fail">Грешка в изпращането</string>
<string name="status_location_update">Местоположението е обновено</string>
+40
Просмотреть файл
@@ -0,0 +1,40 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Traccar ক্লায়েন্ট</string>
<string name="app_logo">Traccar</string>
<string name="settings_id_title">ডিভাইস সনাক্তকারী</string>
<string name="settings_url_title">সার্ভার URL</string>
<string name="settings_url_summary">ট্র্যাকিং সার্ভার URL</string>
<string name="settings_interval_title">ফ্রিকোয়েন্সি</string>
<string name="settings_interval_summary">রিপোর্টিং বিরতি - সেকেন্ডে</string>
<string name="settings_distance_title">দূরত্ব</string>
<string name="settings_distance_summary">রিপোর্টিং বিরতি - মিটারে</string>
<string name="settings_angle_title">অ্যাঙ্গেল</string>
<string name="settings_angle_summary">রিপোর্টিং অ্যাঙ্গেল - ডিগ্রিতে</string>
<string name="settings_status_title">সার্ভিসের অবস্থা</string>
<string name="settings_status_off">চালু</string>
<string name="settings_status_on">বন্ধ</string>
<string name="settings_status_off_summary">সার্ভিস বন্ধ হয়েছে</string>
<string name="settings_status_on_summary">সার্ভিস চলমান</string>
<string name="settings_provider_title">লোকেশন প্রদানকারী</string>
<string name="settings_provider_summary">লোকেশন ডেটা উৎস</string>
<string name="settings_provider_gps">জিপিএস প্রদানকারী</string>
<string name="settings_provider_network">নেটওয়ার্ক প্রদানকারী</string>
<string name="settings_provider_mixed">মিশ্র প্রদানকারী</string>
<string name="settings_foreground_title">ফোরগ্রাউন্ড সার্ভিস</string>
<string name="settings_foreground_summary">সার্ভিসের পূর্ববর্তিতা বৃদ্ধি</string>
<string name="menu_status">অবস্থা</string>
<string name="menu_about">সম্পর্কে / সম্বন্ধে</string>
<string name="menu_shortcuts">শর্টকাট যুক্ত করা</string>
<string name="menu_clear">পরিষ্কার</string>
<string name="about_description">অ্যান্ড্রয়েড ডিভাইসের জন্য রিয়েল টাইম GPS ট্র্যাকার। Traccar সার্ভার এবং অন্যান্য ট্র্যাকিং সিস্টেমগুলির সাথে সামঞ্জস্যপূর্ণ।</string>
<string name="about_license">এই অ্যাপ্লিকেশনটি বিনামূল্যে প্রদানকৃত এবং ওপেন সোর্স, সোর্স কোডটি Apache License Version 2.0 এর অধীনে লাইসেন্স করা এবং GitHub এ রক্ষিত।</string>
<string name="about_web">আরও তথ্যের জন্য www.traccar.org/client দেখুন</string>
<string name="status_send_success">সফলভাবে পাঠান হয়েছে</string>
<string name="status_send_fail">পাঠান ব্যর্থ হয়েছে</string>
<string name="status_location_update">অবস্থান আপডেট</string>
<string name="status_connectivity_change">সংযোগ পরিবর্তন হয়েছে</string>
<string name="hidden_app_name">ডিভাইস সেটিংস</string>
<string name="hidden_alert">অ্যাপ্লিকেশনটি লুকানো আছে। আবার খুলতে ডায়াল করুন 8722227 (TRACCAR)।</string>
<string name="error_msg_invalid_url">দয়া করে একটি ভ্যালিড http:// বা https:// URL লিখুন</string>
</resources>
+8 -14
Просмотреть файл
@@ -2,25 +2,20 @@
<resources>
<string name="app_name">Traccar klient</string>
<string name="app_logo">Traccar</string>
<string name="shortcut_start">Traccar - Spustit</string>
<string name="shortcut_stop">Traccar - Zastavit</string>
<string name="shortcut_sos">Traccar - SOS</string>
<string name="settings_id_title">Identifikátor zařízení</string>
<string name="settings_address_title">Adresa serveru</string>
<string name="settings_address_summary">Doménové jméno nebo IP adresa</string>
<string name="settings_port_title">Port</string>
<string name="settings_port_summary">TCP port sledovacího serveru</string>
<string name="settings_url_title">URL serveru</string>
<string name="settings_url_summary">URL sledovacího serveru</string>
<string name="settings_interval_title">Frekvence</string>
<string name="settings_interval_summary">Reportuji interval v sekundách</string>
<string name="settings_distance_title">Vzdálenost</string>
<string name="settings_distance_summary">Reportuji vzdálenost v metrech</string>
<string name="settings_angle_title">Úhel</string>
<string name="settings_angle_summary">Reportuji úhel ve stupních</string>
<string name="settings_status_title">Stav služby</string>
<string name="settings_status_off">Start</string>
<string name="settings_status_on">Stop</string>
<string name="settings_status_off_summary">Zastaveno</string>
<string name="settings_status_on_summary">Spuštěno</string>
<string name="settings_secure_title">Šifrování</string>
<string name="settings_secure_off">Povolit</string>
<string name="settings_secure_on">Zakázat</string>
<string name="settings_secure_off_summary">Šifrování zakázáno</string>
<string name="settings_secure_on_summary">Šifrování povoleno</string>
<string name="settings_provider_title">Poskytovatel zaměření</string>
<string name="settings_provider_summary">Zdroj zaměřovacích dat</string>
<string name="settings_provider_gps">GPS</string>
@@ -35,12 +30,11 @@
<string name="about_description">GPS sledovací aplikace pracující v reálném čase pro zařízení s operačním systémem Android. Kompatibilní s Traccar serverem a dalšími sledovacími systémy.</string>
<string name="about_license">Tato aplikace je zdarma a open source, zdrojový kód je licencován pod Apache License Version 2.0 a je přístupný na GitHub.</string>
<string name="about_web">Pro více informací navštivte\nwww.traccar.org/client</string>
<string name="status_service_create">Služba vytvořena</string>
<string name="status_service_destroy">Služba zastavena</string>
<string name="status_send_success">Úspěšně odesláno</string>
<string name="status_send_fail">Odesílání selhalo</string>
<string name="status_location_update">Aktualizace pozice</string>
<string name="status_connectivity_change">Změna připojení</string>
<string name="hidden_app_name">Nastavení zařízení</string>
<string name="hidden_alert">Aplikace byla skryta. Pro znovuotevření vytočte 8722227 (TRACCAR)</string>
<string name="error_msg_invalid_url">Prosím vložte platnou http:// nebo https:// URL</string>
</resources>
+10 -16
Просмотреть файл
@@ -2,31 +2,26 @@
<resources>
<string name="app_name">Traccar klient</string>
<string name="app_logo">Traccar</string>
<string name="shortcut_start">Traccar - Start</string>
<string name="shortcut_stop">Traccar - Stop</string>
<string name="shortcut_sos">SOS</string>
<string name="settings_id_title">Indentificere Enhed</string>
<string name="settings_address_title">Server adresse</string>
<string name="settings_address_summary">Domænenavn eller IP adresse</string>
<string name="settings_port_title">Server port</string>
<string name="settings_port_summary">Tracking server TCP port</string>
<string name="settings_id_title">Enheds ID</string>
<string name="settings_url_title">Server Adresse</string>
<string name="settings_url_summary">Tracking server adresse</string>
<string name="settings_interval_title">Tracking interval</string>
<string name="settings_interval_summary">Rapporterings interval i sekunder</string>
<string name="settings_distance_title">Distance</string>
<string name="settings_distance_summary">Rapportering distance i meter</string>
<string name="settings_angle_title">vinkel</string>
<string name="settings_angle_summary">Rapportering i grader</string>
<string name="settings_status_title">Service status</string>
<string name="settings_status_off">Start</string>
<string name="settings_status_on">Stop</string>
<string name="settings_status_off_summary">Service stoppet</string>
<string name="settings_status_on_summary">Service er aktiv</string>
<string name="settings_secure_title">Kryptering</string>
<string name="settings_secure_off">Aktiver</string>
<string name="settings_secure_on">Deaktiver</string>
<string name="settings_secure_off_summary">Kryptering deaktiveret</string>
<string name="settings_secure_on_summary">Kryptering aktiveret</string>
<string name="settings_provider_title">Udbyder</string>
<string name="settings_provider_summary">Udbyder lokaliseringsdata</string>
<string name="settings_provider_gps">GPS udbyder</string>
<string name="settings_provider_network">Netværks udbyder</string>
<string name="settings_provider_mixed">Mixet udbyder</string>
<string name="settings_foreground_title">Forgrunden tjeneste</string>
<string name="settings_foreground_title">Forgrunds tjeneste</string>
<string name="settings_foreground_summary">Øg tjeneste prioritet</string>
<string name="menu_status">Status</string>
<string name="menu_about">Om</string>
@@ -35,12 +30,11 @@
<string name="about_description">Real tid`s GPS tracker for Android enheder. Kompatibel med Traccar Server og andre tracking systemer.</string>
<string name="about_license">Denne application er en fri og open source, source koden er licenseret under Apache License Version 2.0 og tilgængelig på GitHub.</string>
<string name="about_web">For mere information besøg\nwww.traccar.org/client</string>
<string name="status_service_create">Service oprettet</string>
<string name="status_service_destroy">Service ødelagt</string>
<string name="status_send_success">Send succesfuld</string>
<string name="status_send_fail">Send fejlede</string>
<string name="status_location_update">Placering opdateret</string>
<string name="status_connectivity_change">Connectivity forandring</string>
<string name="hidden_app_name">Enheds indstillinger</string>
<string name="hidden_alert">Appen er skjult. For at åbne appen igen skal du ringe til 8722227 (TRACCAR)</string>
<string name="error_msg_invalid_url">Indtast valid http:// eller https:/ adresse</string>
</resources>
+8 -13
Просмотреть файл
@@ -2,14 +2,13 @@
<resources>
<string name="app_name">Traccar Client</string>
<string name="app_logo">Traccar</string>
<string name="shortcut_start">Traccar - Start</string>
<string name="shortcut_stop">Traccar - Stop</string>
<string name="shortcut_sos">Traccar - SOS</string>
<string name="shortcut_start">Dienst starten</string>
<string name="shortcut_stop">Dienst stoppen</string>
<string name="shortcut_sos">SOS senden</string>
<string name="channel_default">Primärkanal</string>
<string name="settings_id_title">Gerätekennung</string>
<string name="settings_address_title">Serveradresse</string>
<string name="settings_address_summary">Domain oder IP Adresse</string>
<string name="settings_port_title">Serverport</string>
<string name="settings_port_summary">Port des Ortungsservers</string>
<string name="settings_url_title">Server URL</string>
<string name="settings_url_summary">Tracking Server URL</string>
<string name="settings_interval_title">Frequenz</string>
<string name="settings_interval_summary">Meldung nach Zeit in Sekunden</string>
<string name="settings_distance_title">Entfernung</string>
@@ -21,11 +20,6 @@
<string name="settings_status_on">Stop</string>
<string name="settings_status_off_summary">Dienst inaktiv</string>
<string name="settings_status_on_summary">Dienst aktiv</string>
<string name="settings_secure_title">Verschlüsselung</string>
<string name="settings_secure_off">Aktivieren</string>
<string name="settings_secure_on">Deaktivieren</string>
<string name="settings_secure_off_summary">Verschlüsselung aktiviert </string>
<string name="settings_secure_on_summary">Verschlüsselung deaktiviert</string>
<string name="settings_provider_title">Datenherkunft</string>
<string name="settings_provider_summary">Quelle der Positionsdaten</string>
<string name="settings_provider_gps">GPS-Provider</string>
@@ -40,7 +34,7 @@
<string name="about_description">Echtzeit GPS Ortung für Android Geräte. Kompatibel mit Traccar Server und anderen Ortungsservern.</string>
<string name="about_license">Dieses Programm ist frei und Open Source. Es ist unter Apache Lizenz Version 2.0 lizenziert und ist auf GitHub verfügbar.</string>
<string name="about_web">Für weitere Informationen besuchen Sie www.traccar.org/client</string>
<string name="status_service_create">Dienst installiert</string>
<string name="status_service_create">Dienst gestartet</string>
<string name="status_service_destroy">Dienst gestoppt</string>
<string name="status_send_success">Erfolgreich gesendet</string>
<string name="status_send_fail">Übertragung fehlerhaft</string>
@@ -48,4 +42,5 @@
<string name="status_connectivity_change">Anschlusswechsel</string>
<string name="hidden_app_name">Geräteeinstellungen</string>
<string name="hidden_alert">Die App wurde versteckt. Um sie wieder zu öffnen, wähle 8722227 (TRACCAR).</string>
<string name="error_msg_invalid_url">Bitte eine gültige http:// oder https:// URL eingeben</string>
</resources>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше