package com.yassirh.digitalocean.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.SyncHttpClient;
import com.yassirh.digitalocean.R;
import com.yassirh.digitalocean.data.DatabaseHelper;
import com.yassirh.digitalocean.data.DropletDao;
import com.yassirh.digitalocean.data.DropletTable;
import com.yassirh.digitalocean.data.ImageDao;
import com.yassirh.digitalocean.data.NetworkDao;
import com.yassirh.digitalocean.model.Account;
import com.yassirh.digitalocean.model.Droplet;
import com.yassirh.digitalocean.model.Image;
import com.yassirh.digitalocean.model.Network;
import com.yassirh.digitalocean.model.Region;
import com.yassirh.digitalocean.model.Size;
import com.yassirh.digitalocean.utils.ApiHelper;
import com.yassirh.digitalocean.utils.MyApplication;
import com.yassirh.digitalocean.utils.PreferencesHelper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import cz.msebera.android.httpclient.HttpEntity;
public class DropletService {
private Context context;
private boolean isRefreshing;
public enum DropletActions{
REBOOT, POWER_CYCLE, SHUTDOWN, POWER_OFF, POWER_ON,
PASSWORD_RESET, RESIZE, SNAPSHOT, RESTORE, REBUILD,
ENABLE_BACKUPS, DISABLE_BACKUPS, RENAME
}
public DropletService(Context context) {
this.context = context;
}
public void setRequiresRefresh(Boolean requireRefresh){
SharedPreferences settings = context.getSharedPreferences("prefrences", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("droplet_require_refresh", requireRefresh);
editor.commit();
}
public Boolean requiresRefresh(){
SharedPreferences settings = context.getSharedPreferences("prefrences", 0);
return settings.getBoolean("droplet_require_refresh", true);
}
public void executeAction(final long dropletId, final DropletActions dropletAction, HashMap<String, String> params){
Account currentAccount = ApiHelper.getCurrentAccount(context);
if(currentAccount == null){
return;
}
String action = getAction(dropletAction);
String url = String.format(Locale.US,"%s/droplets/%d/actions", ApiHelper.API_URL, dropletId);
HashMap<String,Object> options = new HashMap<>();
options.put("type", action);
for (Entry<String, String> param : params.entrySet()) {
options.put(param.getKey(), param.getValue());
}
JSONObject jsonObject = new JSONObject(options);
AsyncHttpClient client = new AsyncHttpClient();
client.addHeader("Authorization", String.format("Bearer %s", currentAccount.getToken()));
HttpEntity entity;
try {
entity = new cz.msebera.android.httpclient.entity.ByteArrayEntity(jsonObject.toString().getBytes("UTF-8"));
client.post(context, url, entity, "application/json", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
if(statusCode == 401){
ApiHelper.showAccessDenied();
}
}
});
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private String getAction(DropletActions dropletAction) {
String action = "";
switch (dropletAction) {
case REBOOT:
action = "reboot";
break;
case POWER_CYCLE:
action = "power_cycle";
break;
case SHUTDOWN:
action = "shutdown";
break;
case POWER_OFF:
action = "power_off";
break;
case POWER_ON:
action = "power_on";
break;
case PASSWORD_RESET:
action = "password_reset";
break;
case RESIZE:
action = "resize";
break;
case SNAPSHOT:
action = "snapshot";
break;
case RESTORE:
action = "restore";
break;
case RENAME:
action = "rename";
break;
case REBUILD:
action = "rebuild";
break;
case ENABLE_BACKUPS:
action = "enable_backups";
break;
case DISABLE_BACKUPS:
action = "disable_backups";
break;
default:
break;
}
return action;
}
public void getAllDropletsFromAPI(final boolean showProgress, boolean synchronous){
Account currentAccount = ApiHelper.getCurrentAccount(context);
if(currentAccount == null){
return;
}
isRefreshing = true;
String url = String.format(Locale.US,"%s/droplets?per_page=%d", ApiHelper.API_URL, Integer.MAX_VALUE);
AsyncHttpClient client;
client = synchronous ? new AsyncHttpClient() : new SyncHttpClient();
client.addHeader("Authorization", String.format("Bearer %s", currentAccount.getToken()));
client.get(url, new AsyncHttpResponseHandler() {
NotificationManager notifyManager;
NotificationCompat.Builder builder;
@Override
public void onStart() {
if(showProgress){
notifyManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(context);
builder.setContentTitle(context.getResources().getString(R.string.synchronising))
.setContentText(context.getResources().getString(R.string.synchronising_droplets))
.setSmallIcon(R.drawable.ic_launcher);
builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT));
Notification note = builder.build();
note.flags |= Notification.FLAG_ONGOING_EVENT;
notifyManager.notify(NotificationsIndexes.NOTIFICATION_GET_ALL_DROPLETS, note);
}
}
@Override
public void onFinish() {
isRefreshing = false;
if(showProgress){
notifyManager.cancel(NotificationsIndexes.NOTIFICATION_GET_ALL_DROPLETS);
}
if(PreferencesHelper.isAutoRestartingDropetsEnabled(context)){
startTurnedOffDroplets();
}
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
if(statusCode == 401){
ApiHelper.showAccessDenied();
}
Toast.makeText(context, "An error occurred - check your internet connection?", Toast.LENGTH_LONG).show();
}
@Override
public void onProgress(long bytesWritten, long totalSize) {
if(showProgress){
builder.setProgress(100, (int) (100*bytesWritten/totalSize), false);
notifyManager.notify(NotificationsIndexes.NOTIFICATION_GET_ALL_DROPLETS, builder.build());
}
}
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
try {
JSONObject jsonObject = new JSONObject(new String(responseBody));
List<Droplet> droplets = new ArrayList<>();
JSONArray dropletsJSONArray = jsonObject.getJSONArray("droplets");
for(int i = 0; i < dropletsJSONArray.length(); i++){
JSONObject dropletJSONObject = dropletsJSONArray.getJSONObject(i);
Droplet droplet = jsonObjectToDroplet(dropletJSONObject);
droplets.add(droplet);
}
DropletService.this.deleteAll();
DropletService.this.saveAll(droplets);
DropletService.this.setRequiresRefresh(true);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
public void deleteAll() {
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
dropletDao.deleteAll();
NetworkDao networkDao = new NetworkDao(DatabaseHelper.getInstance(context));
networkDao.deleteAll();
}
public static Network jsonObjectToNetwork(JSONObject networkJSONObject) throws JSONException {
Network network = new Network();
if(!networkJSONObject.isNull("cidr")){
network.setCidr(networkJSONObject.getString("cidr"));
}
if(!networkJSONObject.isNull("netmask")){
network.setNetmask(networkJSONObject.getString("netmask"));
}
network.setGateway(networkJSONObject.getString("gateway"));
network.setIpAddress(networkJSONObject.getString("ip_address"));
network.setType(networkJSONObject.getString("type"));
return network;
}
public static Droplet jsonObjectToDroplet(JSONObject dropletJSONObject) throws JSONException {
Droplet droplet = new Droplet();
Image image = ImageService.jsonObjectToImage(dropletJSONObject.getJSONObject("image"));
ImageDao imageDao = new ImageDao(DatabaseHelper.getInstance(MyApplication.getAppContext()));
if(imageDao.findById(image.getId()) == null){
image.setInUse(false);
imageDao.create(image);
}
Region region = RegionService.jsonObjectToRegion(dropletJSONObject.getJSONObject("region"));
Size size = new Size();
size.setSlug(dropletJSONObject.getString("size_slug"));
droplet.setId(dropletJSONObject.getLong("id"));
droplet.setName(dropletJSONObject.getString("name"));
droplet.setMemory(dropletJSONObject.getInt("memory"));
droplet.setCpu(dropletJSONObject.getInt("vcpus"));
droplet.setDisk(dropletJSONObject.getInt("disk"));
droplet.setImage(image);
droplet.setRegion(region);
droplet.setSize(size);
droplet.setLocked(dropletJSONObject.getBoolean("locked"));
droplet.setStatus(dropletJSONObject.getString("status"));
List<Network> networks = new ArrayList<>();
JSONObject networksJSONObject = dropletJSONObject.getJSONObject("networks");
JSONArray v4JSONArray = networksJSONObject.getJSONArray("v4");
JSONArray v6JSONArray = networksJSONObject.getJSONArray("v6");
for (int i = 0; i < v4JSONArray.length(); i++) {
JSONObject networkJSONObject = v4JSONArray.getJSONObject(i);
Network network = jsonObjectToNetwork(networkJSONObject);
networks.add(network);
}
for (int i = 0; i < v6JSONArray.length(); i++) {
JSONObject networkJSONObject = v6JSONArray.getJSONObject(i);
Network network = jsonObjectToNetwork(networkJSONObject);
networks.add(network);
}
JSONArray featuresJSONArray = dropletJSONObject.getJSONArray("features");
for (int i = 0; i < featuresJSONArray.length(); i++) {
if("ipv6".equals(featuresJSONArray.getString(i))){
droplet.setIpv6Enabled(true);
}else if("virtio".equals(featuresJSONArray.getString(i))){
droplet.setVirtIoEnabled(true);
}else if("private_networking".equals(featuresJSONArray.getString(i))){
droplet.setPrivateNetworkingEnabled(true);
}else if("backups".equals(featuresJSONArray.getString(i))){
droplet.setBackupsEnabled(true);
}
}
droplet.setNetworks(networks);
try {
droplet.setCreatedAt(ApiHelper.iso8601Format.parse(dropletJSONObject.getString("created_at").replace("Z", "")));
} catch (ParseException e) {
e.printStackTrace();
}
return droplet;
}
protected void saveAll(List<Droplet> droplets) {
NetworkDao networkDao = new NetworkDao(DatabaseHelper.getInstance(context));
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
networkDao.deleteAll();
for (Droplet droplet : droplets) {
long id = dropletDao.createOrUpdate(droplet);
droplet.setId(id);
for (Network network : droplet.getNetworks()) {
network.setDroplet(droplet);
networkDao.createOrUpdate(network);
}
}
DropletService.this.setRequiresRefresh(true);
}
public List<Droplet> getAllDroplets(){
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
NetworkDao networkDao = new NetworkDao(DatabaseHelper.getInstance(context));
List<Droplet> droplets = dropletDao.getAll(null);
for (Droplet droplet : droplets) {
droplet.setNetworks(networkDao.findByDropletId(droplet.getId()));
}
return droplets;
}
public Droplet findById(long id) {
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
NetworkDao networkDao = new NetworkDao(DatabaseHelper.getInstance(context));
Droplet droplet = dropletDao.findById(id);
List<Network> networks = networkDao.findByDropletId(id);
droplet.setNetworks(networks);
return droplet;
}
public void createDroplet(String hostname, Long imageId, String regionSlug, String sizeSlug,
boolean privateNetworking, boolean enableBackups, boolean enableIPv6, String userData, List<Long> selectedSSHKeysIds) {
Account currentAccount = ApiHelper.getCurrentAccount(context);
if(currentAccount == null){
return;
}
String url = String.format("%s/droplets", ApiHelper.API_URL);
HashMap<String,Object> options = new HashMap<>();
options.put("name", hostname);
options.put("region", regionSlug);
options.put("size", sizeSlug);
options.put("image", imageId);
if(selectedSSHKeysIds.size() > 0) {
List<String> keys = new ArrayList<>();
for (Long key: selectedSSHKeysIds) {
keys.add(key.toString());
}
options.put("ssh_keys", keys);
}
options.put("backups", enableBackups);
options.put("ipv6", enableIPv6);
options.put("private_networking", privateNetworking);
if(!userData.equals("")){
options.put("user_data", userData);
}
JSONObject jsonObject = new JSONObject(options);
AsyncHttpClient client = new AsyncHttpClient();
client.addHeader("Authorization", String.format("Bearer %s", currentAccount.getToken()));
try {
HttpEntity entity = new cz.msebera.android.httpclient.entity.ByteArrayEntity(jsonObject.toString().getBytes("UTF-8"));
client.post(context, url, entity, "application/json", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
if(statusCode == 401){
ApiHelper.showAccessDenied();
} else if(statusCode == 422){
try {
JSONObject jsonObject = new JSONObject(new String(responseBody));
Toast.makeText(context, jsonObject.getString("message"), Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
ActionService.trackActions(context);
}
/*public void getDropletFromAPI(long dropletId, final boolean showProgress) {
Account currentAccount = ApiHelper.getCurrentAccount(context);
if(currentAccount == null){
return;
}
String url = String.format(Locale.US, "%s/droplets/%d", ApiHelper.API_URL, dropletId);
AsyncHttpClient client = new AsyncHttpClient();
client.addHeader("Authorization", String.format("Bearer %s", currentAccount.getToken()));
client.get(url, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
JSONObject jsonObject = new JSONObject(new String(responseBody));
JSONObject dropletJSONObject = jsonObject.getJSONObject("droplet");
Droplet droplet = jsonObjectToDroplet(dropletJSONObject);
DropletService.this.update(droplet);
DropletService.this.setRequiresRefresh(true);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
if(statusCode == 401){
ApiHelper.showAccessDenied();
}
}
});
}*/
protected void update(Droplet droplet) {
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
dropletDao.createOrUpdate(droplet);
}
public boolean isRefreshing() {
return isRefreshing;
}
public void destroyDroplet(long dropletId) {
Account currentAccount = ApiHelper.getCurrentAccount(context);
if(currentAccount == null){
return;
}
String url = String.format(Locale.US,"%s/droplets/%d", ApiHelper.API_URL, dropletId);
AsyncHttpClient client = new AsyncHttpClient();
client.addHeader("Authorization", String.format("Bearer %s", currentAccount.getToken()));
client.delete(url, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
requiresRefresh();
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
if(statusCode == 401){
ApiHelper.showAccessDenied();
}
}
});
}
public void startTurnedOffDroplets() {
DropletDao dropletDao = new DropletDao(DatabaseHelper.getInstance(context));
List<Droplet> droplets = dropletDao.getAllByProperty(DropletTable.STATUS, "off");
for (Droplet droplet : droplets) {
executeAction(droplet.getId(), DropletActions.POWER_ON, new HashMap<String, String>());
}
}
}