package org.droidplanner.android.droneshare; import android.app.IntentService; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; import android.support.v4.util.Pair; import android.text.TextUtils; import com.geeksville.apiproxy.rest.RESTClient; import org.droidplanner.android.DroidPlannerApp; import org.droidplanner.android.R; import org.droidplanner.android.droneshare.data.DroneShareDB; import org.droidplanner.android.utils.Utils; import org.droidplanner.android.utils.prefs.DroidPlannerPrefs; import java.io.File; import java.io.IOException; import java.util.List; import timber.log.Timber; /** * Provides delayed uploads to the DroneShare service. * * If you send any intent to this service it will scan the tlog directory and * upload any complete tlogs it finds. */ public class UploaderService extends IntentService { private static final String DRONESHARE_PRIVACY = "DEFAULT"; static final String apiKey = "2d38fb2e.72afe7b3761d5ee6346c178fdd6b680f"; private static final int ONGOING_UPLOAD_NOTIFICATION_ID = 123; private static final int UPLOAD_STATUS_NOTIFICATION_ID = 124; public static final String ACTION_CHECK_FOR_DRONESHARE_UPLOADS = Utils.PACKAGE_NAME + ".ACTION_CHECK_FOR_DRONESHARE_UPLOADS"; private DroidPlannerPrefs dpPrefs; private DroneShareDB droneShareDb; private NotificationManagerCompat notifyManager; private Notification failedUploadNotification; public UploaderService() { super("DroneShare Uploader"); } @Override public void onCreate() { super.onCreate(); final Context context = getApplicationContext(); dpPrefs = DroidPlannerPrefs.getInstance(context); notifyManager = NotificationManagerCompat.from(context); droneShareDb = ((DroidPlannerApp) getApplication()).getDroneShareDatabase(); } @Override protected void onHandleIntent(Intent intent) { final String action = intent.getAction(); if (action == null) { return; } // Check if droneshare is enabled, and the login credentials set before trying to do anything. if (dpPrefs.isDroneshareEnabled()) { switch (action) { case ACTION_CHECK_FOR_DRONESHARE_UPLOADS: List<Pair<Long, Uri>> dataToUpload = droneShareDb.getDataToUpload(dpPrefs.getDroneshareLogin()); if(!dataToUpload.isEmpty()){ if (NetworkConnectivityReceiver.isNetworkAvailable(getApplicationContext())) { Timber.i("Preparing droneshare data for upload"); doUploads(dataToUpload); } else { Timber.w("Network offline.. Rescheduling droneshare data upload"); // Activating the network connectivity receiver so we can be restarted when connectivity is restored. Timber.d("Activating connectivity receiver"); NetworkConnectivityReceiver.enableConnectivityReceiver(getApplicationContext(), true); } } break; } } } private NotificationCompat.Builder generateNotificationBuilder() { return new NotificationCompat.Builder(getApplicationContext()) .setContentTitle(getString(R.string.uploader_notification_title)) .setSmallIcon(R.drawable.ic_stat_notify) .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_HIGH); } private void doUploads(List<Pair<Long, Uri>> dataToUpload) { String login = dpPrefs.getDroneshareLogin(); String password = dpPrefs.getDronesharePassword(); if (!login.isEmpty() && !password.isEmpty()) { final Notification notification = generateNotificationBuilder() .setContentText("Uploading tlog data") .build(); startForeground(ONGOING_UPLOAD_NOTIFICATION_ID, notification); try { int numUploaded = 0; for (Pair<Long, Uri> datumInfo : dataToUpload) { long uploadId = datumInfo.first; Uri dataUri = datumInfo.second; File uploadFile = new File(dataUri.getPath()); if (uploadFile.isFile()) { Timber.i("Starting upload for " + uploadFile); String url = RESTClient.doUpload(uploadFile, login, password, null, apiKey, DRONESHARE_PRIVACY); if (url != null) { numUploaded++; } onUploadSuccess(uploadFile, url, numUploaded); } else{ Timber.w("TLog data file is not available."); } droneShareDb.commitUploadedData(uploadId, System.currentTimeMillis()); } } catch (IOException e) { Timber.e(e, "Unable to complete tlog data upload"); onUploadFailure(e); } stopForeground(true); } } private void onUploadSuccess(File f, String viewURL, int numUploaded) { if (viewURL == null) { Timber.i("Server thought flight was boring"); notifyManager.cancel(ONGOING_UPLOAD_NOTIFICATION_ID); } else { Timber.i("Upload success: " + f + " url=" + viewURL); // Attach the view URL final PendingIntent pIntent = PendingIntent.getActivity(UploaderService.this, 0, new Intent(Intent.ACTION_VIEW, Uri.parse(viewURL)), PendingIntent.FLAG_UPDATE_CURRENT); final Intent sendIntent = new Intent(Intent.ACTION_SEND).putExtra( Intent.EXTRA_TEXT, viewURL).setType("text/plain"); final PendingIntent sendPIntent = PendingIntent.getActivity(UploaderService.this, 0, sendIntent, PendingIntent.FLAG_UPDATE_CURRENT); final NotificationCompat.Builder notifBuilder = generateNotificationBuilder() .setContentText(getString(R.string.uploader_success_message)) .setContentIntent(pIntent) // Attach a web link .addAction(android.R.drawable.ic_menu_set_as, "Web", pIntent) // Add a share link .addAction(android.R.drawable.ic_menu_share, "Share", sendPIntent); if (numUploaded > 1) notifBuilder.setNumber(numUploaded); updateUploadStatusNotification(notifBuilder.build()); } } private void onUploadFailure(Exception ex) { String msg = ex.getMessage(); if(TextUtils.isEmpty(msg)) { msg = "Upload Failed"; } if (failedUploadNotification == null) { failedUploadNotification = generateNotificationBuilder().setContentText(msg) .setSubText(getString(R.string.uploader_fail_retry_message)).build(); } updateUploadStatusNotification(failedUploadNotification); if (!NetworkConnectivityReceiver.isNetworkAvailable(getApplicationContext())) { // Activating the network connectivity receiver so we can be // restarted when // connectivity is restored. Timber.d("Activating connectivity receiver"); NetworkConnectivityReceiver.enableConnectivityReceiver(getApplicationContext(), true); } } private void updateUploadStatusNotification(Notification notification) { notifyManager.notify(UPLOAD_STATUS_NOTIFICATION_ID, notification); } public static void kickStart(Context context){ if(DroidPlannerPrefs.getInstance(context).isDroneshareEnabled()) { context.startService( new Intent(context, UploaderService.class) .setAction(ACTION_CHECK_FOR_DRONESHARE_UPLOADS)); } } }