Привет Хабр!
Однажды была поставлена задача разработать движок для расчета времени начала заката и рассвета на основе местоположения телефона.
Кроме того, чтобы облегчить себе работу, меня загрузили аномальными математическими формулами, из-за которых ничего не укладывалось в моей голове.
Итак, я решил поискать альтернативу этим адским формулам.
Покопавшись в Гугле, я нашел интересный сайт: http://www.earthtools.org/ .
Он предоставляет несколько веб-сервисов, связанных с работой с картами.
Одним из них был сервис, которому вы предоставляете в качестве входных данных широту, долготу и дату, а в ответ получаете xml со следующим содержимым:
Исходя из этого, задача значительно упрощается:
1. Получить координаты устройства;
2. Сгенерируйте GET-запрос;
3. Отправить запрос;
4. Получите ответ (наш XML);
5. Разобрать XML для получения необходимых данных;
6. Отобразите результат.
Сразу предупрежу, что никакого хардкорного кода здесь не будет, а только простой движок.
Приступим, начнем с разрешений в AndroidManifest.xml.
Далее получаем координаты устройства.<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" />
Сразу отмечу, что точные координаты на практике совершенно не нужны для задач такого рода, поэтому можно использовать даже те координаты, которые нам дает сотовая сеть.
Поскольку здесь нет супер-UI, а крайне маленький движок, поэтому у Activity есть только кнопка «Обновить», а под ней TextView для отображения результатов.
Я также удалил названия пакетов и импорта.
public class MainActivity extends ActionBarActivity implements View.OnClickListener {
private TextView helloWorld;
private Button update;
private LocationListener locationListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUI();
initLocationListener();
}
private void requestLocationUpdate() {
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
List<String> allProviders = locationManager.getAllProviders();
for (String provider : allProviders) {
helloWorld.append(provider + "\n");
}
String bestProvider = locationManager.getBestProvider(createGPSCriteria(), true);
helloWorld.append("Best: " + bestProvider + "\n");
locationManager.requestSingleUpdate(bestProvider, locationListener, null);
}
private void initLocationListener() {
locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
Calendar calendar = Calendar.getInstance();
int day = calendar.get(Calendar.DATE);
int month = calendar.get(Calendar.MONTH) + 1;
SunAsyncTask sunAsyncTask = new SunAsyncTask(location.getLatitude(), location.getLongitude(), day, month);
try {
HttpResponse response = sunAsyncTask.execute().
get();
String stringEntity = EntityUtils.toString(response.getEntity());
SunXmlParser sunXmlParser = new SunXmlParser(stringEntity);
helloWorld.append("Sunset: " + sunXmlParser.parse(SunXmlParser.SUNSET) + "\n");
helloWorld.append("Sunrise: " + sunXmlParser.parse(SunXmlParser.SUNRISE) + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
}
private void initUI() {
helloWorld = (TextView) findViewById(R.id.hello_world);
update = (Button) findViewById(R.id.update);
update.setOnClickListener(this);
}
private Criteria createGPSCriteria() {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setCostAllowed(false);
return criteria;
}
@Override
public void onClick(View clickedView) {
int id = clickedView.getId();
if (id == R.id.update) {
requestLocationUpdate();
}
}
}
Здесь мы сначала инициализируем UI, затем инициализируем LocationListener, который должен будет реагировать на изменение координат, а именно при изменении позиции он запустит AsyncTask, который сформирует запрос, отправит его и получит ответ.
После инициализации нам необходимо выбрать провайдера, у которого мы будем брать широту и долготу нашего местоположения.
Здесь нет ничего сложного, есть готовый метод getBestProvider, который делает это по критериям, которые мы ему передаем в первом аргументе.
Критерии — это класс, в который мы помещаем параметры провайдера, которого хотим получить.
В этом примере я задал критерий выбора бесплатного провайдера, который будет работать даже при плохом сигнале и с «нормальной» точностью определения местоположения.
Ну и, соответственно, нажав кнопку Update у провайдера, мы запрашиваем обновление местоположения, на что LocationListener отвечает, вызывая AsynkTask, о котором я вам сейчас расскажу.
public class SunAsyncTask extends AsyncTask<Void, Void, HttpResponse> {
private double latitude;
private double longitude;
private int day;
private int month;
public SunAsyncTask(double latitude, double longitude, int day, int month) {
this.latitude = latitude;
this.longitude = longitude;
this.day = day;
this.month = month;
}
private String createUrl() {
// http://www.earthtools.org/sun/ <latitude>/<longitude>/<day>/<month>/<timezone>/<dst>
StringBuilder stringBuilder = new StringBuilder(" http://www.earthtools.org/sun/ ");
stringBuilder.append(String.valueOf(latitude)).
append("/"); stringBuilder.append(String.valueOf(longitude)).
append("/"); stringBuilder.append(String.valueOf(day)).
append("/"); stringBuilder.append(String.valueOf(month)).
append("/"); stringBuilder.append("99").
append("/");
stringBuilder.append("0");
Log.d("URL", stringBuilder.toString());
return stringBuilder.toString();
}
@Override
protected HttpResponse doInBackground(Void. params) {
HttpResponse response = null;
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(createUrl());
try {
response = client.execute(get);
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
}
Здесь тоже все просто.
Напомню, как я уже говорил, для получения времени нам нужны координаты и дата дня заката и рассвета, которые мы хотим получить.
Метод createUrl генерирует URL-адрес для запроса GET, но параметров здесь несколько больше, чем я писал ранее.
А именно: часовой пояс и летнее время.
Итак, с часовым поясом все понятно, вы можете указать часовой пояс, в котором хотите произвести расчет, но если вы подставите в качестве часового пояса «99», то он будет определен автоматически на основе тех координат, которые вы передаете.
Соответственно я поставил 99. Ну dst учитывает летнее время (0 - нет, 1 - да).
Итак, мы должны получить XML с результатом.
Теперь его нужно разобрать.
Мы будем использовать XmlPullParser как обычно; для тех, кто не знаком, советую с ним ознакомиться, так как он имеет ряд «особенностей».
Ну и, собственно, класс, который разбирает закат и восход солнца.
public class SunXmlParser {
private String xmlString;
private XmlPullParser parser;
public static final String SUNRISE = "sunrise";
public static final String SUNSET = "sunset";
public SunXmlParser(String xmlString) throws XmlPullParserException {
this.xmlString = xmlString;
parser = Xml.newPullParser();
parser.setInput(new StringReader(xmlString));
}
public void setXmlString(String xmlString) throws XmlPullParserException {
this.xmlString = xmlString;
}
public String parse(String tag) throws XmlPullParserException, IOException {
parser.setInput(new StringReader(xmlString));
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_TAG && parser.getName().
equalsIgnoreCase(tag)) {
parser.next();
return parser.getText();
}
eventType = parser.next();
}
return "Not found";
}
}
Вот и все, собственно.
Проблема решена, результат на экране:
Теги: #Android #локация #закат #Восход #Разработка Android
-
Оптимизация Php: Все, Что Вам Нужно Знать
19 Oct, 24 -
Вебмани Мини Для Linux
19 Oct, 24 -
Загадка Для Хабраузера
19 Oct, 24