Полный листинг Java проекта GPS-трекера, сгенерированный в https://chat.qwen.ai/ по единому запросу на генерирование кода (AI-powered Code Generation) Отладка запроса заняла примерно 3 часа с перерывами, что примерно в 40 раз быстрее, чем написание аналогичного проекта "вручную". Приложение можно легко переделать для определения координат не пл GPS, а по базовым GSM вышкам. Приложение может быть полезно для дальних путешествий. Период отправки SNS в пять минут выбран для отладки. Ниже приведён 'минимально работающий Java-код' для Android-приложения `GeoPosition`, соответствующий вашим требованиям: 'Важно': Начиная с Android 10 (API 29), 'прямой доступ к внешнему хранилищу ограничен'. Для записи в `Download` используется 'MediaStore API'. Также 'отправка SMS без явного действия пользователя (например, клика) может быть ограничена' на некоторых устройствах (особенно от Samsung или с Android 12+), но технически разрешено, если есть разрешение `SEND_SMS`. Direct Link: https://chat.qwen.ai/s/7c09b59c-bc54-4ca4-9120-c7aba69af8d9?fev=0.1.18 1. `AndroidManifest.xml` В следующем тексте в каждой строке: - в самом начале надо поставить
- и заменить КАЖДЫЙ пробел ТОЛЬКО в начале каждой строки до начала текста на сущность &nbsp; Текст: 2. `MainActivity.java` package com.example.geoposition; import android.Manifest; import android.content.ContentValues; import android.content.pm.PackageManager; import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.telephony.SmsManager; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import org.json.JSONObject; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_CODE = 100; private static final String PHONE_NUMBER = "+79100000000"; // замените на реальный номер private TextView tvTime, tvCoords, tvSatellites; private LocationManager locationManager; private int satellitesUsed = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvTime = findViewById(R.id.tvTime); tvCoords = findViewById(R.id.tvCoords); tvSatellites = findViewById(R.id.tvSatellites); // Запрос разрешений requestPermissionsIfNeeded(); } private void requestPermissionsIfNeeded() { String[] permissions; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { permissions = new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.SEND_SMS }; } else { permissions = new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.SEND_SMS, Manifest.permission.WRITE_EXTERNAL_STORAGE }; } boolean needRequest = false; for (String perm : permissions) { if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) { needRequest = true; break; } } if (needRequest) { ActivityCompat.requestPermissions(this, permissions, PERMISSIONS_REQUEST_CODE); } else { startAppLogic(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSIONS_REQUEST_CODE) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startAppLogic(); } else { Toast.makeText(this, "Требуются все разрешения!", Toast.LENGTH_LONG).show(); finish(); } } } private void startAppLogic() { // Удаляем старый файл, если есть deletePositionFile(); // Отправляем SMS старта sendSms("Start GeoPosition"); // Инициализация GPS initGps(); // Запуск периодической отправки каждые 5 минут schedulePeriodicUpdate(); } private void deletePositionFile() { // На Android 10+ используем MediaStore // Удаление файла через запрос по URI не поддерживается напрямую, // но при создании нового файла с тем же именем — старый будет заменён. // В нашем случае просто не читаем старый, а перезаписываем при первой записи. } private void initGps() { locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } // Получаем координаты сразу Location lastKnown = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastKnown != null) { onLocationChanged(lastKnown); } locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener); // Слушатель спутников (работает не на всех устройствах) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { locationManager.registerGnssStatusCallback(gnssStatusCallback); } else { locationManager.addGpsStatusListener(gpsStatusListener); } } private final LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(@NonNull Location location) { onLocationChanged(location); } }; private void onLocationChanged(Location location) { String time = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy", Locale.getDefault()).format(new Date(location.getTime())); String coords = String.format(Locale.getDefault(), "Lat: %.6f, Lng: %.6f", location.getLatitude(), location.getLongitude()); tvTime.setText("Время: " + time); tvCoords.setText("Координаты: " + coords); tvSatellites.setText("Спутники: " + satellitesUsed); // Записываем и отправляем savePositionToFile(time, location.getLatitude(), location.getLongitude(), satellitesUsed); sendSms(String.format("Geo: %s | %s | Sat: %d", time, coords, satellitesUsed)); } // Для Android < N private final GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() { @Override public void onGpsStatusChanged(int event) { if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) { GpsStatus status = locationManager.getGpsStatus(null); int count = 0; for (GpsStatus.NmeaSentence ignored : status.getSatellites()) { count++; } satellitesUsed = count; } } }; // Для Android >= N private final LocationManager.GnssStatusCallback gnssStatusCallback = new LocationManager.GnssStatusCallback() { @Override public void onSatelliteStatusChanged(@NonNull android.location.GnssStatus status) { satellitesUsed = status.getSatelliteCount(); // Примечание: это — общее количество видимых, не обязательно используемых runOnUiThread(() -> tvSatellites.setText("Спутники: " + satellitesUsed)); } }; private void savePositionToFile(String time, double lat, double lng, int sat) { try { JSONObject json = new JSONObject(); json.put("time", time); json.put("latitude", lat); json.put("longitude", lng); json.put("satellites", sat); String jsonString = json.toString() + "\n"; ContentValues values = new ContentValues(); values.put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, "Position.txt"); values.put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "text/plain"); values.put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, android.os.Environment.DIRECTORY_DOWNLOADS); Uri uri = getContentResolver().insert(android.provider.MediaStore.Files.getContentUri("external"), values); if (uri != null) { OutputStream out = getContentResolver().openOutputStream(uri); if (out != null) { out.write(jsonString.getBytes()); out.close(); } } } catch (Exception e) { e.printStackTrace(); } } private void sendSms(String message) { try { if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED) { SmsManager.getDefault().sendTextMessage(PHONE_NUMBER, null, message, null, null); } } catch (Exception e) { e.printStackTrace(); } } private void schedulePeriodicUpdate() { final Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { Location last = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (last != null) { onLocationChanged(last); } } handler.postDelayed(this, 5 * 60 * 1000); // 5 минут } }; handler.post(runnable); } } 3. `activity_main.xml` Что делает код: 1. Запрашивает: - `ACCESS_FINE_LOCATION` - `SEND_SMS` - `WRITE_EXTERNAL_STORAGE` (только до Android 10) 2. Удаляет старый `Position.txt` (фактически, перезаписывает при первой записи). 3. Отправляет SMS: "Start GeoPosition" 4. Получает GPS-координаты и отображает: - Время фиксации - Координаты - Количество спутников 5. Каждые 5 минут: - Сохраняет данные в `Download/Position.txt` в формате JSON (добавляя новую строку) - Отправляет SMS Замечания: - На 'Android 11+' файл `Position.txt` будет создан в папке 'Downloads', но не перезаписывается при каждом запуске, а дополняется. Чтобы гарантированно стереть — нужно найти URI существующего файла и удалить его. Это сложно через MediaStore. Если важно — можно использовать `ACTION_OPEN_DOCUMENT_TREE` или работать в частной папке приложения. - 'Спутники': точное количество используемых спутников получить нельзя. Android даёт только количество 'видимых'. - 'SMS без подтверждения' может блокироваться системой (особенно на Android 12+). - Для теста замените `PHONE_NUMBER` на ваш реальный номер.