ElectronReminder/app/src/main/java/ru/vfilippov/electronreminder/ElectronReminderService.java

278 lines
10 KiB
Java

package ru.vfilippov.electronreminder;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import simple.SimpleXml;
import simple.SimpleXmlNode;
/**
* This {@code IntentService} does the app's actual work.
* {@code RecheckAlarmReceiver} (a {@code WakefulBroadcastReceiver}) holds a
* partial wake lock for this service while the service does its work. When the
* service is finished, it calls {@code completeWakefulIntent()} to release the
* wake lock.
*/
public class ElectronReminderService extends IntentService
{
public ElectronReminderService()
{
super("ElectronReminderService");
}
public static final String TAG = "Electron Reminder";
private String token = null;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
@Override
protected void onHandleIntent(Intent intent)
{
String action = intent.getAction();
FileOutputStream fs = null;
InputStream is = null;
try
{
if (action.equals("load_cities"))
{
File file = new File(getFilesDir(), "suburban_cities.xml");
if (!file.exists() || file.lastModified() < System.currentTimeMillis()-86400*7*1000)
{
is = downloadUrl("http://static.rasp.yandex.net/data/export/suburban_cities.xml");
fs = new FileOutputStream(file);
copyStream(is, fs);
}
Intent i = new Intent("cities_loaded");
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
}
else if (action == "load_stations")
{
int cityId = intent.getIntExtra("city_id", 0);
is = downloadUrl("http://mobile.rasp.yandex.net/export/suburban/city/"+cityId+"/stations?uuid="+token);
File file = new File(getFilesDir(), "stations_"+cityId+".xml");
fs = new FileOutputStream(file);
copyStream(is, fs);
Intent i = new Intent("stations_loaded");
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
}
else if (action == "load_trips")
{
int from = intent.getIntExtra("from", 0);
int to = intent.getIntExtra("to", 0);
String date = intent.getStringExtra("date");
getTrips(from, to, date, false);
Intent i = new Intent("trips_loaded");
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
}
else if (action == "refresh_all")
{
File file = new File(getFilesDir(), "reminders.xml");
if (file.exists())
{
FileInputStream fis = new FileInputStream(file);
SimpleXmlNode remindersRoot = SimpleXml.parse(fis);
fis.close();
int i = 0;
Date d = new Date();
String curTime = new SimpleDateFormat("hh:mm").format(d);
SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");
for (SimpleXmlNode reminder: remindersRoot.childrenByName.get("reminder"))
{
int from = Integer.valueOf(reminder.attributes.get("from"));
int to = Integer.valueOf(reminder.attributes.get("to"));
String time = reminder.attributes.get("time");
String date;
// FIXME Check for different date?
if (time.compareTo(curTime) >= 0)
date = ymd.format(new Date(d.getTime()+86400000));
else
date = ymd.format(d);
List<SimpleXmlNode> segments = getTrips(from, to, date, true);
boolean found = false;
for (SimpleXmlNode seg: segments)
{
if (String.valueOf(from).equals(seg.attributes.get("from")) &&
String.valueOf(to).equals(seg.attributes.get("to")) &&
(date+" "+time).equals(seg.attributes.get("departure")))
{
found = true;
break;
}
}
if (!found)
sendNotification(i, from, to, reminder.attributes.get("fromName"), reminder.attributes.get("toName"), time);
i++;
}
}
}
}
catch(IOException e)
{
Toast.makeText(getApplicationContext(), "Network error: "+e.getMessage(), Toast.LENGTH_LONG).show();
}
catch (XmlPullParserException e)
{
Toast.makeText(getApplicationContext(), "Error parsing XML: "+e.getMessage(), Toast.LENGTH_LONG).show();
}
finally
{
try
{
if (is != null)
is.close();
if (fs != null)
fs.close();
}
catch(IOException e)
{
}
}
// Release the wake lock provided by the BroadcastReceiver.
RecheckAlarmReceiver.completeWakefulIntent(intent);
}
private List<SimpleXmlNode> getTrips(int from, int to, String date, boolean decode) throws IOException, XmlPullParserException
{
if (token == null)
initToken();
FileOutputStream fs = null;
try
{
byte[] trips = loadFromNetwork(
"http://mobile.rasp.yandex.net/export/suburban/trip/"+from+"/"+to+
"/?date="+date+"&tomorrow_upto=12&uuid="+token
);
File file = new File(getFilesDir(), "trips_"+from+"_"+to+".xml");
fs = new FileOutputStream(file);
fs.write(trips);
if (decode)
{
ByteArrayInputStream bis = new ByteArrayInputStream(trips);
SimpleXmlNode node = SimpleXml.parse(bis);
bis.close();
return node.childrenByName.get("segment");
}
}
finally
{
if (fs != null)
fs.close();
}
return null;
}
private void copyStream(InputStream is, OutputStream os) throws IOException
{
byte[] buf = new byte[0x10000];
int r;
while ((r = is.read(buf)) > 0)
os.write(buf, 0, r);
}
private boolean initToken() throws IOException, XmlPullParserException
{
// FIXME Store token?
SimpleXmlNode node = SimpleXml.parse(downloadUrl("http://mobile.rasp.yandex.net/rasp/startup?app_platform=android&app_version=200"));
List<SimpleXmlNode> l = node.childrenByName.get("uuid");
if (l != null)
{
token = l.get(0).value;
return true;
}
return false;
}
// Post a notification indicating suburban changes
private void sendNotification(int notificationId, int from, int to, String fromName, String toName, String time)
{
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(this, MainActivity.class);
i.putExtra("from", from);
i.putExtra("to", to);
i.putExtra("time", time);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
String msg = getString(R.string.changes_details, fromName, toName, time);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(getString(R.string.changes_detected))
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(notificationId, mBuilder.build());
}
// Given a URL string, initiate a fetch operation.
private byte[] loadFromNetwork(String urlString) throws IOException
{
InputStream stream = null;
try
{
stream = downloadUrl(urlString);
return readStream(stream);
}
finally
{
if (stream != null)
stream.close();
}
}
/**
* Given a string representation of a URL, sets up a connection and gets
* an input stream.
* @param urlString A string representation of a URL.
* @return An InputStream retrieved from a successful HttpURLConnection.
* @throws IOException
*/
private InputStream downloadUrl(String urlString) throws IOException
{
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Start the query
conn.connect();
InputStream stream = conn.getInputStream();
return stream;
}
private byte[] readStream(InputStream is) throws IOException
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
copyStream(is, bos);
return bos.toByteArray();
}
}