Java tutorial
/* * Cos android client for Cozy Cloud * * Copyright (C) 2016 Hamza Abdelkebir * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.codeplumbers.cosi.services; import android.Manifest; import android.app.IntentService; import android.app.NotificationManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.support.v4.app.ActivityCompat; import android.support.v4.content.WakefulBroadcastReceiver; import android.support.v7.app.NotificationCompat; import android.util.Base64; import android.util.Log; import org.apache.commons.io.IOUtils; import org.greenrobot.eventbus.EventBus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; import java.util.ArrayList; import java.util.List; import eu.codeplumbers.cosi.R; import eu.codeplumbers.cosi.db.models.Device; import eu.codeplumbers.cosi.db.models.Sms; import eu.codeplumbers.cosi.services.events.MessageEvent; import eu.codeplumbers.cosi.services.events.SmsSyncEvent; import eu.codeplumbers.cosi.utils.DateUtils; import static eu.codeplumbers.cosi.utils.Constants.REFRESH; import static eu.codeplumbers.cosi.utils.Constants.SERVICE_ERROR; import static eu.codeplumbers.cosi.utils.Constants.SYNC_MESSAGE; /** * Created by thor on 10/29/16. */ public class CosiSmsService extends IntentService { private List<Sms> allSms; private String errorMessage; private String authHeader; private String designUrl; private String syncUrl; private int notification_id = 1337; private NotificationManager mNotifyManager; private NotificationCompat.Builder mBuilder; private boolean mSyncRunning; private boolean stopSync; public CosiSmsService() { super("CosiSmsService"); stopSync = false; mSyncRunning = false; } public CosiSmsService(String name) { super(name); } @Override protected void onHandleIntent(Intent intent) { // Do the task here Log.i(CosiSmsService.class.getName(), "Cosi Service running"); // Release the wake lock provided by the WakefulBroadcastReceiver. WakefulBroadcastReceiver.completeWakefulIntent(intent); allSms = new ArrayList<>(); Device device = Device.registeredDevice(); // cozy register device url designUrl = device.getUrl() + "/ds-api/request/sms/all/"; syncUrl = device.getUrl() + "/ds-api/data/"; // concatenate username and password with colon for authentication final String credentials = device.getLogin() + ":" + device.getPassword(); authHeader = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); showNotification(); if (isNetworkAvailable()) { try { // read local sms log first readSmsDatabase(); EventBus.getDefault().post(new SmsSyncEvent(SYNC_MESSAGE, "Getting remote messages from Cozy...")); getRemoteMessages(); mBuilder.setContentText(getString(R.string.lbl_sms_sync_done)); mBuilder.setProgress(0, 0, false); mNotifyManager.notify(notification_id, mBuilder.build()); if (!allSms.isEmpty()) { mNotifyManager.cancel(notification_id); sendChangesToCozy(); EventBus.getDefault().post(new SmsSyncEvent(REFRESH, "Sync OK")); } else { EventBus.getDefault().post(new MessageEvent(SERVICE_ERROR, "Sync failed")); } } catch (Exception e) { e.printStackTrace(); mSyncRunning = false; EventBus.getDefault().post(new MessageEvent(SERVICE_ERROR, e.getLocalizedMessage())); } } else { mSyncRunning = false; EventBus.getDefault().post(new MessageEvent(SERVICE_ERROR, "No Internet connection")); mBuilder.setContentText("Sync failed because no Internet connection was available"); mNotifyManager.notify(notification_id, mBuilder.build()); } } private void readSmsDatabase() { //Fetches the complete call log in descending order. i.e recent calls appears first. if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED) { Uri message = Uri.parse("content://sms/"); ContentResolver cr = getContentResolver(); Cursor c = cr.query(message, null, null, null, null); int totalSMS = c.getCount(); if (c.moveToFirst()) { for (int i = 0; i < totalSMS; i++) { mBuilder.setProgress(totalSMS, i, false); mNotifyManager.notify(notification_id, mBuilder.build()); EventBus.getDefault() .post(new SmsSyncEvent(SYNC_MESSAGE, getString(R.string.lbl_sms_status_read_phone))); String systemId = c.getString(c.getColumnIndexOrThrow("_id")); String address = c.getString(c.getColumnIndexOrThrow("address")); String body = c.getString(c.getColumnIndexOrThrow("body")); String readState = c.getString(c.getColumnIndex("read")); String dateAndTime = c.getString(c.getColumnIndexOrThrow("date")); String type = c.getString(c.getColumnIndexOrThrow("type")); Sms sms = Sms.getBySystemId(systemId); if (sms == null) { sms = new Sms(); sms.setRemoteId(""); } sms.setBody(body); sms.setAddress(address.replace(" ", "").replace("-", "")); sms.setDateAndTime(DateUtils.formatDate(dateAndTime)); sms.setSystemId(systemId); sms.setType(Integer.valueOf(type)); sms.setReadState(Boolean.parseBoolean(readState)); sms.setDeviceId( sms.getRemoteId() != "" ? sms.getDeviceId() : Device.registeredDevice().getLogin()); sms.save(); allSms.add(sms); c.moveToNext(); } } // else { // throw new RuntimeException("You have no SMS"); // } c.close(); } else { EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, getString(R.string.permission_denied_sms))); stopSelf(); } } private List<Sms> getRemoteMessages() { allSms.clear(); URL urlO = null; try { urlO = new URL(designUrl); HttpURLConnection conn = (HttpURLConnection) urlO.openConnection(); conn.setConnectTimeout(5000); conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); conn.setRequestProperty("Authorization", authHeader); conn.setDoInput(true); conn.setRequestMethod("POST"); // read the response int status = conn.getResponseCode(); InputStream in = null; if (status >= HttpURLConnection.HTTP_BAD_REQUEST) { in = conn.getErrorStream(); } else { in = conn.getInputStream(); } StringWriter writer = new StringWriter(); IOUtils.copy(in, writer, "UTF-8"); String result = writer.toString(); JSONArray jsonArray = new JSONArray(result); if (jsonArray != null) { if (jsonArray.length() == 0) { EventBus.getDefault() .post(new SmsSyncEvent(SYNC_MESSAGE, "Your Cozy has no Text messages stored.")); Sms.setAllUnsynced(); } else { for (int i = 0; i < jsonArray.length(); i++) { mBuilder.setProgress(jsonArray.length(), i, false); mNotifyManager.notify(notification_id, mBuilder.build()); EventBus.getDefault() .post(new SmsSyncEvent(SYNC_MESSAGE, getString(R.string.lbl_sms_status_read_cozy))); JSONObject smsJson = jsonArray.getJSONObject(i).getJSONObject("value"); Sms sms = Sms.getBySystemIdAddressAndBody(smsJson.get("systemId").toString(), smsJson.getString("address"), smsJson.getString("body")); if (sms == null) { sms = new Sms(smsJson); } else { sms.setRemoteId(smsJson.getString("_id")); sms.setSystemId(smsJson.getString("systemId")); sms.setAddress(smsJson.getString("address")); sms.setBody(smsJson.getString("body")); if (smsJson.has("readState")) { sms.setReadState(smsJson.getBoolean("readState")); } sms.setDateAndTime(smsJson.getString("dateAndTime")); sms.setType(smsJson.getInt("type")); } sms.save(); allSms.add(sms); } } } else { errorMessage = "Failed to parse API response"; EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, "Failed to parse API response")); } in.close(); conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); errorMessage = e.getLocalizedMessage(); } catch (ProtocolException e) { errorMessage = e.getLocalizedMessage(); e.printStackTrace(); } catch (IOException e) { errorMessage = e.getLocalizedMessage(); e.printStackTrace(); } catch (JSONException e) { errorMessage = e.getLocalizedMessage(); e.printStackTrace(); } return allSms; } public void sendChangesToCozy() { List<Sms> unSyncedSms = Sms.getAllUnsynced(); int i = 0; for (Sms sms : unSyncedSms) { URL urlO = null; try { JSONObject jsonObject = sms.toJsonObject(); mBuilder.setProgress(unSyncedSms.size(), i, false); mBuilder.setContentText("Syncing " + jsonObject.getString("docType") + ":"); mNotifyManager.notify(notification_id, mBuilder.build()); EventBus.getDefault() .post(new SmsSyncEvent(SYNC_MESSAGE, getString(R.string.lbl_sms_status_read_phone))); String remoteId = jsonObject.getString("remoteId"); String requestMethod = ""; if (remoteId.isEmpty()) { urlO = new URL(syncUrl); requestMethod = "POST"; } else { urlO = new URL(syncUrl + remoteId + "/"); requestMethod = "PUT"; } HttpURLConnection conn = (HttpURLConnection) urlO.openConnection(); conn.setConnectTimeout(5000); conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); conn.setRequestProperty("Authorization", authHeader); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestMethod(requestMethod); // set request body jsonObject.remove("remoteId"); long objectId = jsonObject.getLong("id"); jsonObject.remove("id"); OutputStream os = conn.getOutputStream(); os.write(jsonObject.toString().getBytes("UTF-8")); os.flush(); // read the response InputStream in = new BufferedInputStream(conn.getInputStream()); StringWriter writer = new StringWriter(); IOUtils.copy(in, writer, "UTF-8"); String result = writer.toString(); JSONObject jsonObjectResult = new JSONObject(result); if (jsonObjectResult != null && jsonObjectResult.has("_id")) { result = jsonObjectResult.getString("_id"); sms.setRemoteId(result); sms.save(); } in.close(); conn.disconnect(); } catch (MalformedURLException e) { EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, e.getLocalizedMessage())); e.printStackTrace(); stopSelf(); } catch (ProtocolException e) { EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, e.getLocalizedMessage())); e.printStackTrace(); stopSelf(); } catch (IOException e) { EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, e.getLocalizedMessage())); e.printStackTrace(); stopSelf(); } catch (JSONException e) { EventBus.getDefault().post(new SmsSyncEvent(SERVICE_ERROR, e.getLocalizedMessage())); e.printStackTrace(); stopSelf(); } i++; } } private boolean isNetworkAvailable() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService( Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } public void showNotification() { mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mBuilder = new NotificationCompat.Builder(this); mBuilder.setContentTitle(getString(R.string.lbl_sms)) .setContentText(getString(R.string.lbl_notes_ongoing_sync)).setSmallIcon(R.drawable.ic_action_email) .setOngoing(true); } }