diff --git a/MIDevTest/.gitignore b/MIDevTest/.gitignore
new file mode 100644
index 0000000..c6cbe56
--- /dev/null
+++ b/MIDevTest/.gitignore
@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/MIDevTest/.idea/.name b/MIDevTest/.idea/.name
new file mode 100644
index 0000000..997f953
--- /dev/null
+++ b/MIDevTest/.idea/.name
@@ -0,0 +1 @@
+MI Dev Test
\ No newline at end of file
diff --git a/MIDevTest/.idea/compiler.xml b/MIDevTest/.idea/compiler.xml
new file mode 100644
index 0000000..9a8b7e5
--- /dev/null
+++ b/MIDevTest/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/copyright/profiles_settings.xml b/MIDevTest/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/MIDevTest/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/gradle.xml b/MIDevTest/.idea/gradle.xml
new file mode 100644
index 0000000..a332c57
--- /dev/null
+++ b/MIDevTest/.idea/gradle.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/misc.xml b/MIDevTest/.idea/misc.xml
new file mode 100644
index 0000000..95f0f03
--- /dev/null
+++ b/MIDevTest/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/modules.xml b/MIDevTest/.idea/modules.xml
new file mode 100644
index 0000000..592dc88
--- /dev/null
+++ b/MIDevTest/.idea/modules.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/runConfigurations.xml b/MIDevTest/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/MIDevTest/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/.idea/vcs.xml b/MIDevTest/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/MIDevTest/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/README.md b/MIDevTest/README.md
new file mode 100644
index 0000000..1a26983
--- /dev/null
+++ b/MIDevTest/README.md
@@ -0,0 +1,14 @@
+# Android MIDevTest App
+
+## Implementations
+- Get data from server
+- Post new data to server
+- Put (update) data to server
+- Delete data from server
+- CRUD data in local app db
+- Asynchronous http calls
+- Asynchronous loading of photos in a list
+- Image data memory caching
+
+## Tests
+- I have only tested this on my Android phone (Note 3, Android 5.0)
\ No newline at end of file
diff --git a/MIDevTest/app/.gitignore b/MIDevTest/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MIDevTest/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MIDevTest/app/build.gradle b/MIDevTest/app/build.gradle
new file mode 100644
index 0000000..cfbbb18
--- /dev/null
+++ b/MIDevTest/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ applicationId "com.janibanez.midevtest"
+ minSdkVersion 19
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.android.support:design:23.1.1'
+ compile project(':database')
+ compile project(':server')
+}
diff --git a/MIDevTest/app/proguard-rules.pro b/MIDevTest/app/proguard-rules.pro
new file mode 100644
index 0000000..d9fbada
--- /dev/null
+++ b/MIDevTest/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/jwgibanez/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/MIDevTest/app/src/androidTest/java/com/janibanez/midevtest/ApplicationTest.java b/MIDevTest/app/src/androidTest/java/com/janibanez/midevtest/ApplicationTest.java
new file mode 100644
index 0000000..5519bcc
--- /dev/null
+++ b/MIDevTest/app/src/androidTest/java/com/janibanez/midevtest/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.janibanez.midevtest;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/AndroidManifest.xml b/MIDevTest/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..89a13bf
--- /dev/null
+++ b/MIDevTest/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceDisplayActivity.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceDisplayActivity.java
new file mode 100644
index 0000000..be9594d
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceDisplayActivity.java
@@ -0,0 +1,197 @@
+package com.janibanez.midevtest;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.janibanez.midevtest.asynctasks.GetBitmapAsyncTask;
+import com.janibanez.midevtest.models.ViewHolder;
+import com.janibanez.midevtest.utilities.DialogUtilities;
+import com.janibanez.midevtest.utilities.ImageCache;
+import com.janibanez.server.ICallback;
+import com.janibanez.server.MiApi;
+import com.janibanez.server.models.Device;
+
+import java.io.IOException;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class DeviceDisplayActivity extends AppCompatActivity {
+
+ private static final int REQUEST_EDIT = 100;
+
+ Device mData;
+ ImageCache mImageCache;
+
+ LinearLayout mImageLayout;
+ TextView mName, mAndroidId, mSnippet, mCarrier;
+ ImageView mImage;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_display_device);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ MainApplication application = (MainApplication) getApplication();
+ mImageCache = application.getImageCache();
+
+ mName = (TextView) findViewById(R.id.name);
+ mAndroidId = (TextView) findViewById(R.id.android_id);
+ mSnippet = (TextView) findViewById(R.id.snippet);
+ mCarrier = (TextView) findViewById(R.id.carrier);
+ mImageLayout = (LinearLayout) findViewById(R.id.li_image);
+ mImage = (ImageView) findViewById(R.id.image);
+
+ mData = (Device) getIntent().getSerializableExtra("device");
+
+ initFields();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_display_device, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_edit:
+ Intent intent = new Intent(this, DeviceEditActivity.class);
+ intent.putExtra("device", mData);
+ startActivityForResult(intent, REQUEST_EDIT);
+ break;
+ case R.id.action_delete:
+ showConfirmation();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_EDIT:
+ if (resultCode == MainActivity.RESULT_REFRESH) {
+ // set result to update main activity
+ setResult(MainActivity.RESULT_REFRESH);
+
+ Device updatedDevice = (Device) data.getSerializableExtra("device");
+ if (updatedDevice != null) {
+ // update view
+ mData = updatedDevice;
+ initFields();
+ }
+ }
+ break;
+ }
+ }
+
+ private void initFields() {
+ if (mData != null) {
+ if (!TextUtils.isEmpty(mData.name))
+ mName.setText(mData.name);
+
+ if (!TextUtils.isEmpty(mData.androidId))
+ mAndroidId.setText(String.valueOf(mData.androidId));
+
+ if (!TextUtils.isEmpty(mData.snippet))
+ mSnippet.setText(mData.snippet);
+
+ if (!TextUtils.isEmpty(mData.carrier))
+ mCarrier.setText(mData.carrier);
+
+ if (!TextUtils.isEmpty(mData.imageUrl))
+ loadImage(mData.imageUrl);
+ }
+ }
+
+ private void showConfirmation() {
+ DialogUtilities.showConfirmation(
+ this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_delete_device),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // neutral click
+ dialog.dismiss();
+ }
+ }, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // positive click
+ deleteDevice();
+ dialog.dismiss();
+ }
+ });
+ }
+
+ private void deleteDevice() {
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.DeleteDevice, mData.id, null, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ DeviceDisplayActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Object response) throws IOException {
+ setResult(MainActivity.RESULT_REFRESH);
+ DeviceDisplayActivity.this.finish();
+ }
+ });
+ }
+
+ private void loadImage(String url) {
+
+ Bitmap bitmap = mImageCache.getBitmapFromMemCache(url);
+
+ if (bitmap != null) {
+ mImageLayout.setVisibility(View.VISIBLE);
+ mImage.setImageBitmap(bitmap);
+ } else {
+ GetBitmapAsyncTask task = new GetBitmapAsyncTask(url, null, new GetBitmapAsyncTask.GetBitmapCallback() {
+ @Override
+ public void onSuccess(String url, ViewHolder holder, Bitmap bitmap) {
+ if (bitmap != null) {
+ mImageLayout.setVisibility(View.VISIBLE);
+ mImage.setImageBitmap(bitmap);
+ }
+ }
+ });
+ task.execute();
+ }
+ }
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceEditActivity.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceEditActivity.java
new file mode 100644
index 0000000..9348eb4
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/DeviceEditActivity.java
@@ -0,0 +1,210 @@
+package com.janibanez.midevtest;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.janibanez.midevtest.utilities.DialogUtilities;
+import com.janibanez.server.ICallback;
+import com.janibanez.server.MiApi;
+import com.janibanez.server.models.Device;
+
+import org.w3c.dom.Text;
+
+import java.io.IOException;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DeviceEditActivity extends AppCompatActivity {
+
+ Device mData;
+
+ EditText mName, mAndroidId, mSnippet, mCarrier, mImageUrl;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_edit_device);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ mData = (Device) getIntent().getSerializableExtra("device");
+
+ mName = (EditText) findViewById(R.id.name);
+ mAndroidId = (EditText) findViewById(R.id.android_id);
+ mSnippet = (EditText) findViewById(R.id.snippet);
+ mCarrier = (EditText) findViewById(R.id.carrier);
+ mImageUrl = (EditText) findViewById(R.id.image_url);
+
+ initFields();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_edit_device, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_create:
+ if (mData != null) {
+ if (mData.id > 0) {
+ // put to update device
+ updateDevice();
+ break;
+ }
+ }
+ // post to create new device
+ createDevice();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void initFields() {
+ if (mData != null) {
+ if (!TextUtils.isEmpty(mData.name))
+ mName.setText(mData.name);
+
+ if (!TextUtils.isEmpty(mData.androidId))
+ mAndroidId.setText(mData.androidId);
+
+ if (!TextUtils.isEmpty(mData.snippet))
+ mSnippet.setText(mData.snippet);
+
+ if (!TextUtils.isEmpty(mData.carrier))
+ mCarrier.setText(mData.carrier);
+
+ if (!TextUtils.isEmpty(mData.imageUrl))
+ mImageUrl.setText(mData.imageUrl);
+ }
+ }
+
+ private boolean isInfoValid() {
+
+ mData.name = mName.getText().toString();
+ mData.snippet = mSnippet.getText().toString();
+ mData.carrier = mCarrier.getText().toString();
+ mData.imageUrl = mImageUrl.getText().toString();
+ mData.androidId = mAndroidId.getText().toString();
+
+ if (TextUtils.isEmpty(mData.name)) {
+ Toast.makeText(this, "Name is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.androidId)) {
+ Toast.makeText(this, "Android ID cannot be empty or 0.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.snippet)) {
+ Toast.makeText(this, "Snippet is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.carrier)) {
+ Toast.makeText(this, "Carrier is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ //if (TextUtils.isEmpty(mData.imageUrl)) {
+ // Toast.makeText(this, "Image URL is empty.", Toast.LENGTH_LONG).show();
+ // return false;
+ //}
+
+ return true;
+ }
+
+ private void createDevice() {
+
+ if (mData == null) {
+ mData = new Device();
+ }
+
+ if (!isInfoValid())
+ // abort
+ return;
+
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.CreateDevice, 0, mData, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ DeviceEditActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Object response) throws IOException {
+ setResult(MainActivity.RESULT_REFRESH);
+ DeviceEditActivity.this.finish();
+ }
+ });
+ }
+
+ private void updateDevice() {
+
+ if (!isInfoValid())
+ // abort
+ return;
+
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.UpdateDevice, mData.id, mData, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ DeviceEditActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Device response) throws IOException {
+ Intent intent = new Intent();
+ intent.putExtra("device", response);
+ setResult(MainActivity.RESULT_REFRESH, intent);
+ DeviceEditActivity.this.finish();
+ }
+ });
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainActivity.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainActivity.java
new file mode 100644
index 0000000..dd0f196
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainActivity.java
@@ -0,0 +1,205 @@
+package com.janibanez.midevtest;
+
+import android.app.ProgressDialog;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.janibanez.database.helpers.DeviceDaoHelper;
+import com.janibanez.database.helpers.VersionDaoHelper;
+import com.janibanez.database.models.DbDevice;
+import com.janibanez.database.models.DbVersion;
+import com.janibanez.midevtest.adapters.MainPagerAdapter;
+import com.janibanez.midevtest.fragments.DevicesFragment;
+import com.janibanez.midevtest.fragments.VersionsFragment;
+import com.janibanez.server.ICallback;
+import com.janibanez.server.MiApi;
+import com.janibanez.server.models.Db;
+import com.janibanez.server.models.Device;
+import com.janibanez.server.models.Version;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity {
+
+ public static final int REQUEST_DISPLAY_DEVICE = 100;
+ public static final int REQUEST_DISPLAY_VERSION = 101;
+
+ public static final int REQUEST_EDIT_DEVICE = 200;
+ public static final int REQUEST_EDIT_VERSION = 201;
+
+ public static final int RESULT_REFRESH = 300;
+
+ MainPagerAdapter mPagerAdapter;
+ ProgressDialog mProgessDialog;
+ TabLayout mTabLayout;
+ ViewPager mViewPager;
+
+ Db mData;
+ List mUpdateListeners = new ArrayList<>();
+
+ public interface MainUpdateListener {
+ void onUpdate(Db response);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mPagerAdapter = new MainPagerAdapter(this, getSupportFragmentManager());
+
+ mPagerAdapter.add(new MainPagerAdapter.FragmentInfo("Devices", DevicesFragment.class.getName()));
+ mPagerAdapter.add(new MainPagerAdapter.FragmentInfo("Versions", VersionsFragment.class.getName()));
+
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ mViewPager.setAdapter(mPagerAdapter);
+
+ mTabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
+ mTabLayout.setupWithViewPager(mViewPager);
+
+ mProgessDialog = new ProgressDialog(this);
+ mProgessDialog.setMessage("Loading...");
+ mProgessDialog.setIndeterminate(true);
+ mProgessDialog.setCancelable(false);
+
+ getData();
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Intent intent;
+ switch (item.getItemId()) {
+ case R.id.action_add_device:
+ intent = new Intent(this, DeviceEditActivity.class);
+ startActivityForResult(intent, REQUEST_EDIT_DEVICE);
+ break;
+ case R.id.action_add_version:
+ intent = new Intent(this, VersionEditActivity.class);
+ startActivityForResult(intent, REQUEST_EDIT_VERSION);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_DISPLAY_DEVICE:
+ case REQUEST_DISPLAY_VERSION:
+ case REQUEST_EDIT_DEVICE:
+ case REQUEST_EDIT_VERSION:
+ if (resultCode == RESULT_REFRESH) {
+ getData();
+ }
+ break;
+ }
+
+ }
+
+ private void getData() {
+
+ mProgessDialog.show();
+
+ MiApi api = new MiApi(this);
+ api.call(MiApi.Action.GetDb, 0, null, new ICallback() {
+ @Override
+ public void onFailure(final Throwable throwable) {
+ // need to run updates on UI thread
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mProgessDialog.dismiss();
+
+ for (int i=0; i < mUpdateListeners.size(); i++) {
+ mUpdateListeners.get(i).onUpdate(null);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(final Db response) throws IOException {
+ // need to run updates on UI thread
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mProgessDialog.dismiss();
+
+ // clear previous db data
+ DeviceDaoHelper.deleteDevices(MainActivity.this);
+ VersionDaoHelper.deleteVersions(MainActivity.this);
+
+ // add all latest devices
+ if (response.devices != null) {
+ for (Device device : response.devices) {
+ ContentValues values = new ContentValues();
+ values.put(DbDevice.COLUMN_ID, device.id);
+ values.put(DbDevice.COLUMN_ANDROID_ID, device.androidId);
+ values.put(DbDevice.COLUMN_NAME, device.name);
+ values.put(DbDevice.COLUMN_SNIPPET, device.snippet);
+ values.put(DbDevice.COLUMN_CARRIER, device.carrier);
+ values.put(DbDevice.COLUMN_IMAGE_URL, device.imageUrl);
+ DeviceDaoHelper.insertDevice(MainActivity.this, values);
+ }
+ }
+
+ // add all latest versions
+ if (response.android != null) {
+ for (Version version : response.android) {
+ ContentValues values = new ContentValues();
+ values.put(DbVersion.COLUMN_ID, version.id);
+ values.put(DbVersion.COLUMN_NAME, version.name);
+ values.put(DbVersion.COLUMN_VERSION, version.version);
+ values.put(DbVersion.COLUMN_CODENAME, version.codename);
+ values.put(DbVersion.COLUMN_TARGET, version.target);
+ values.put(DbVersion.COLUMN_DISTRIBUTION, version.distribution);
+ VersionDaoHelper.insertVersion(MainActivity.this, values);
+ }
+ }
+
+ for (int i=0; i < mUpdateListeners.size(); i++) {
+ mUpdateListeners.get(i).onUpdate(response);
+ }
+
+ /* TESTS
+ List versions = VersionDaoHelper.getVersions(MainActivity.this);
+
+ VersionDaoHelper.deleteVersions(MainActivity.this);
+
+ versions = VersionDaoHelper.getVersions(MainActivity.this);
+
+ List devices = DeviceDaoHelper.getDevices(MainActivity.this);
+
+ DeviceDaoHelper.deleteDevices(MainActivity.this);
+
+ devices = DeviceDaoHelper.getDevices(MainActivity.this);
+ */
+ }
+ });
+ }
+ });
+ }
+
+ public void addUpdateListener(MainUpdateListener listener) {
+ mUpdateListeners.add(listener);
+ }
+
+ public void removeUpdateListener(MainUpdateListener listener) {
+ mUpdateListeners.remove(listener);
+ }
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainApplication.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainApplication.java
new file mode 100644
index 0000000..45d847b
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/MainApplication.java
@@ -0,0 +1,37 @@
+package com.janibanez.midevtest;
+
+import android.app.Application;
+import android.os.Environment;
+
+import com.janibanez.midevtest.utilities.ImageCache;
+
+import java.io.File;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class MainApplication extends Application {
+
+ ImageCache mImageCache;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ // implement image caching, using 20% of max runtime memory
+ ImageCache.ImageCacheParams params = new ImageCache.ImageCacheParams(this, 0.2f, getFileDirectory() + "/cache");
+ mImageCache = new ImageCache(params);
+ }
+
+ public ImageCache getImageCache() {
+ return mImageCache;
+ }
+
+ private static File getFileDirectory() {
+ File directory = new File(Environment.getExternalStorageDirectory(), "SpotifyChartsFiles");
+ if (!directory.exists()) {
+ directory.mkdirs();
+ }
+ return directory;
+ }
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionDisplayActivity.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionDisplayActivity.java
new file mode 100644
index 0000000..d16c362
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionDisplayActivity.java
@@ -0,0 +1,163 @@
+package com.janibanez.midevtest;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import com.janibanez.midevtest.utilities.DialogUtilities;
+import com.janibanez.server.ICallback;
+import com.janibanez.server.MiApi;
+import com.janibanez.server.models.Version;
+
+import java.io.IOException;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class VersionDisplayActivity extends AppCompatActivity {
+
+ private static final int REQUEST_EDIT = 100;
+
+ Version mData;
+
+ TextView mName, mCodename, mVersion, mTarget, mDistribution;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_display_version);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ mName = (TextView) findViewById(R.id.name);
+ mCodename = (TextView) findViewById(R.id.codename);
+ mVersion = (TextView) findViewById(R.id.version);
+ mTarget = (TextView) findViewById(R.id.target);
+ mDistribution = (TextView) findViewById(R.id.distribution);
+
+ mData = (Version) getIntent().getSerializableExtra("version");
+
+ initFields();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_display_version, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_edit:
+ Intent intent = new Intent(this, VersionEditActivity.class);
+ intent.putExtra("version", mData);
+ startActivityForResult(intent, REQUEST_EDIT);
+ break;
+ case R.id.action_delete:
+ showConfirmation();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_EDIT:
+ if (resultCode == MainActivity.RESULT_REFRESH) {
+ // set result to update main activity
+ setResult(MainActivity.RESULT_REFRESH);
+
+ Version updatedVersion = (Version) data.getSerializableExtra("version");
+ if (updatedVersion != null) {
+ // update view
+ mData = updatedVersion;
+ initFields();
+ }
+ }
+ break;
+ }
+ }
+
+ private void initFields() {
+ if (mData != null) {
+ if (!TextUtils.isEmpty(mData.name))
+ mName.setText(mData.name);
+
+ if (!TextUtils.isEmpty(mData.codename))
+ mCodename.setText(mData.codename);
+
+ if (!TextUtils.isEmpty(mData.version))
+ mVersion.setText(mData.version);
+
+ if (!TextUtils.isEmpty(mData.target))
+ mTarget.setText(mData.target);
+
+ if (!TextUtils.isEmpty(mData.distribution))
+ mDistribution.setText(mData.distribution);
+ }
+ }
+
+ private void showConfirmation() {
+ DialogUtilities.showConfirmation(
+ this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_delete_version),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // neutral click
+ dialog.dismiss();
+ }
+ }, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // positive click
+ deleteVersion();
+ dialog.dismiss();
+ }
+ });
+ }
+
+ private void deleteVersion() {
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.DeleteVersion, mData.id, null, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ VersionDisplayActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Object response) throws IOException {
+ setResult(MainActivity.RESULT_REFRESH);
+ VersionDisplayActivity.this.finish();
+ }
+ });
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionEditActivity.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionEditActivity.java
new file mode 100644
index 0000000..11a9e8a
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/VersionEditActivity.java
@@ -0,0 +1,208 @@
+package com.janibanez.midevtest;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.janibanez.midevtest.utilities.DialogUtilities;
+import com.janibanez.server.ICallback;
+import com.janibanez.server.MiApi;
+import com.janibanez.server.models.Version;
+
+import java.io.IOException;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class VersionEditActivity extends AppCompatActivity {
+
+ Version mData;
+
+ EditText mName, mVersion, mCodename, mTarget, mDistribution;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_edit_version);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ mData = (Version) getIntent().getSerializableExtra("version");
+
+ mName = (EditText) findViewById(R.id.name);
+ mVersion = (EditText) findViewById(R.id.version);
+ mCodename = (EditText) findViewById(R.id.codename);
+ mTarget = (EditText) findViewById(R.id.target);
+ mDistribution = (EditText) findViewById(R.id.distribution);
+
+ initFields();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_edit_version, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ break;
+ case R.id.action_create:
+ if (mData != null) {
+ if (mData.id > 0) {
+ // put to update version
+ updateVersion();
+ break;
+ }
+ }
+ // post to create new version
+ createVersion();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void initFields() {
+ if (mData != null) {
+ if (!TextUtils.isEmpty(mData.name))
+ mName.setText(mData.name);
+
+ if (!TextUtils.isEmpty(mData.codename))
+ mCodename.setText(mData.codename);
+
+ if (!TextUtils.isEmpty(mData.version))
+ mVersion.setText(mData.version);
+
+ if (!TextUtils.isEmpty(mData.target))
+ mTarget.setText(mData.target);
+
+ if (!TextUtils.isEmpty(mData.distribution))
+ mDistribution.setText(mData.distribution);
+ }
+ }
+
+ private boolean isInfoValid() {
+
+ mData.name = mName.getText().toString();
+ mData.version = mVersion.getText().toString();
+ mData.codename = mCodename.getText().toString();
+ mData.target = mTarget.getText().toString();
+ mData.distribution = mDistribution.getText().toString();
+
+ if (TextUtils.isEmpty(mData.name)) {
+ Toast.makeText(this, "Name is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.version)) {
+ Toast.makeText(this, "Version is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.codename)) {
+ Toast.makeText(this, "Codename is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.target)) {
+ Toast.makeText(this, "Target is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ if (TextUtils.isEmpty(mData.distribution)) {
+ Toast.makeText(this, "Distribution is empty.", Toast.LENGTH_LONG).show();
+ return false;
+ }
+
+ return true;
+ }
+
+ private void createVersion() {
+
+ if (mData == null) {
+ mData = new Version();
+ }
+
+ if (!isInfoValid())
+ // abort
+ return;
+
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.CreateVersion, 0, mData, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ VersionEditActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Object response) throws IOException {
+ setResult(MainActivity.RESULT_REFRESH);
+ VersionEditActivity.this.finish();
+ }
+ });
+ }
+
+ private void updateVersion() {
+
+ if (!isInfoValid())
+ // abort
+ return;
+
+ MiApi api = new MiApi(this);
+
+ api.call(MiApi.Action.UpdateVersion, mData.id, mData, new ICallback() {
+ @Override
+ public void onFailure(Throwable throwable) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogUtilities.showMessageDialog(
+ VersionEditActivity.this,
+ getString(R.string.dialog_title_alert),
+ getString(R.string.dialog_message_something_went_wrong),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onResponse(Version response) throws IOException {
+ Intent intent = new Intent();
+ intent.putExtra("version", response);
+ setResult(MainActivity.RESULT_REFRESH, intent);
+ VersionEditActivity.this.finish();
+ }
+ });
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/DevicesListAdapter.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/DevicesListAdapter.java
new file mode 100644
index 0000000..8cd2014
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/DevicesListAdapter.java
@@ -0,0 +1,114 @@
+package com.janibanez.midevtest.adapters;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.janibanez.midevtest.MainApplication;
+import com.janibanez.midevtest.R;
+import com.janibanez.midevtest.asynctasks.GetBitmapAsyncTask;
+import com.janibanez.midevtest.models.ViewHolder;
+import com.janibanez.midevtest.utilities.ImageCache;
+import com.janibanez.server.models.Device;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class DevicesListAdapter extends ArrayAdapter {
+
+ List mData;
+ ImageCache mImageCache;
+
+ // use view holder pattern
+ public static class DeviceViewHolder extends ViewHolder {
+ TextView name, snippet;
+ ProgressBar progress;
+ ImageView image;
+ String photoUrl;
+ }
+
+ public DevicesListAdapter(Context context, ArrayList list) {
+ super(context, R.layout.list_item_device, list);
+ mData = list;
+
+ MainApplication application = (MainApplication) ((Activity) context).getApplication();
+ mImageCache = application.getImageCache();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ Device data = mData.get(position);
+
+ DeviceViewHolder holder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_device, parent, false);
+
+ holder = new DeviceViewHolder();
+
+ holder.name = (TextView) convertView.findViewById(R.id.name);
+ holder.snippet = (TextView) convertView.findViewById(R.id.snippet);
+ holder.progress = (ProgressBar) convertView.findViewById(R.id.progress);
+ holder.image = (ImageView) convertView.findViewById(R.id.image);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (DeviceViewHolder) convertView.getTag();
+ }
+
+ holder.name.setText(data.name);
+ holder.snippet.setText(data.snippet);
+ holder.photoUrl = data.imageUrl;
+
+ if (holder.photoUrl != null) {
+ // look up for cached image
+ Bitmap bitmap = mImageCache.getBitmapFromMemCache(holder.photoUrl);
+
+ if (bitmap != null) {
+ // show cached image
+ holder.image.setImageBitmap(bitmap);
+ holder.image.setVisibility(View.VISIBLE);
+ holder.progress.setVisibility(View.INVISIBLE);
+ } else {
+ // try to fetch image, and cache it
+ holder.image.setVisibility(View.INVISIBLE);
+ holder.progress.setVisibility(View.VISIBLE);
+ GetBitmapAsyncTask task = new GetBitmapAsyncTask(holder.photoUrl, holder, new GetBitmapAsyncTask.GetBitmapCallback() {
+ @Override
+ public void onSuccess(String url, ViewHolder holder, Bitmap bitmap) {
+ mImageCache.addBitmapToMemCache(url, bitmap);
+
+ DeviceViewHolder deviceViewHolder = (DeviceViewHolder) holder;
+
+ // only apply bitmap to correct view
+ if (TextUtils.equals(url, deviceViewHolder.photoUrl)) {
+ deviceViewHolder.image.setImageBitmap(bitmap);
+ deviceViewHolder.image.setVisibility(View.VISIBLE);
+ deviceViewHolder.progress.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ task.execute();
+ }
+ } else {
+ holder.image.setImageBitmap(null);
+ holder.image.setVisibility(View.VISIBLE);
+ holder.progress.setVisibility(View.INVISIBLE);
+ }
+
+ return convertView;
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/MainPagerAdapter.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/MainPagerAdapter.java
new file mode 100644
index 0000000..6564334
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/MainPagerAdapter.java
@@ -0,0 +1,54 @@
+package com.janibanez.midevtest.adapters;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class MainPagerAdapter extends FragmentStatePagerAdapter {
+
+ Context mContext;
+ List mFragmentInfos;
+
+ public MainPagerAdapter(Context context, FragmentManager fm) {
+ super(fm);
+ mContext = context;
+ mFragmentInfos = new ArrayList<>();
+ }
+
+ public static class FragmentInfo {
+ String fragmentName;
+ String tabName;
+
+ public FragmentInfo(String tname, String fname) {
+ this.fragmentName = fname;
+ this.tabName = tname;
+ }
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return Fragment.instantiate(mContext, mFragmentInfos.get(position).fragmentName);
+ }
+
+ @Override
+ public int getCount() {
+ return mFragmentInfos.size();
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mFragmentInfos.get(position).tabName;
+ }
+
+ public void add(FragmentInfo fragmentInfo) {
+ mFragmentInfos.add(fragmentInfo);
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/VersionsListAdapter.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/VersionsListAdapter.java
new file mode 100644
index 0000000..06054ba
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/adapters/VersionsListAdapter.java
@@ -0,0 +1,64 @@
+package com.janibanez.midevtest.adapters;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.janibanez.midevtest.R;
+import com.janibanez.server.models.Version;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class VersionsListAdapter extends ArrayAdapter {
+
+ List mData;
+
+ // use view holder pattern
+ public static class ViewHolder {
+ TextView name, codename, version;
+ }
+
+ public VersionsListAdapter(Context context, ArrayList list) {
+ super(context, R.layout.list_item_version, list);
+ mData = list;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ Version data = mData.get(position);
+
+ ViewHolder holder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_version, parent, false);
+
+ holder = new ViewHolder();
+
+ holder.name = (TextView) convertView.findViewById(R.id.name);
+ holder.version = (TextView) convertView.findViewById(R.id.version);
+ holder.codename = (TextView) convertView.findViewById(R.id.codename);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.name.setText(TextUtils.concat("Name: ", data.name));
+ holder.version.setText(TextUtils.concat("Version: ", data.version));
+ holder.codename.setText(TextUtils.concat("Codename: ", data.codename));
+
+ return convertView;
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/asynctasks/GetBitmapAsyncTask.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/asynctasks/GetBitmapAsyncTask.java
new file mode 100644
index 0000000..276f49e
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/asynctasks/GetBitmapAsyncTask.java
@@ -0,0 +1,51 @@
+package com.janibanez.midevtest.asynctasks;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+
+import com.janibanez.midevtest.models.ViewHolder;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class GetBitmapAsyncTask extends AsyncTask {
+
+ private String url;
+ private ViewHolder holder;
+ private GetBitmapCallback callback;
+
+ public interface GetBitmapCallback {
+ void onSuccess(String url, ViewHolder holder, Bitmap bitmap);
+ }
+
+ public GetBitmapAsyncTask(String url, ViewHolder holder, GetBitmapCallback callback) {
+ this.url = url;
+ this.holder = holder;
+ this.callback = callback;
+ }
+
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ Bitmap bitmap = null;
+
+ try {
+ InputStream is = (InputStream) new URL(url).getContent();
+ bitmap = BitmapFactory.decodeStream(is);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return bitmap;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ if (callback != null) {
+ callback.onSuccess(url, holder, bitmap);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/DevicesFragment.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/DevicesFragment.java
new file mode 100644
index 0000000..ddf83bb
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/DevicesFragment.java
@@ -0,0 +1,78 @@
+package com.janibanez.midevtest.fragments;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import com.janibanez.database.helpers.DeviceDaoHelper;
+import com.janibanez.midevtest.DeviceDisplayActivity;
+import com.janibanez.midevtest.MainActivity;
+import com.janibanez.midevtest.R;
+import com.janibanez.midevtest.adapters.DevicesListAdapter;
+import com.janibanez.server.models.Db;
+import com.janibanez.server.models.Device;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class DevicesFragment extends Fragment implements MainActivity.MainUpdateListener, AdapterView.OnItemClickListener {
+
+ MainActivity mActivity;
+ ListView mList;
+ DevicesListAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActivity = (MainActivity) getActivity();
+ mActivity.addUpdateListener(this);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View rootView = inflater.inflate(R.layout.fragment_devices, container, false);
+
+ mAdapter = new DevicesListAdapter(mActivity, new ArrayList());
+
+ mList = (ListView) rootView.findViewById(R.id.list);
+ mList.setAdapter(mAdapter);
+ mList.setOnItemClickListener(this);
+
+ return rootView;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mActivity.removeUpdateListener(this);
+ }
+
+ @Override
+ public void onUpdate(Db response) {
+ if (response != null) {
+ mAdapter.clear();
+
+ // get all devices from db
+ List devices = DeviceDaoHelper.getDevices(getActivity());
+
+ mAdapter.addAll(devices);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Intent intent = new Intent(mActivity, DeviceDisplayActivity.class);
+ intent.putExtra("device", mAdapter.getItem(position));
+ mActivity.startActivityForResult(intent, MainActivity.REQUEST_DISPLAY_DEVICE);
+ }
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/VersionsFragment.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/VersionsFragment.java
new file mode 100644
index 0000000..42efd3d
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/fragments/VersionsFragment.java
@@ -0,0 +1,79 @@
+package com.janibanez.midevtest.fragments;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import com.janibanez.database.helpers.VersionDaoHelper;
+import com.janibanez.midevtest.MainActivity;
+import com.janibanez.midevtest.R;
+import com.janibanez.midevtest.VersionDisplayActivity;
+import com.janibanez.midevtest.adapters.VersionsListAdapter;
+import com.janibanez.server.models.Db;
+import com.janibanez.server.models.Version;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class VersionsFragment extends Fragment implements MainActivity.MainUpdateListener, AdapterView.OnItemClickListener {
+
+ MainActivity mActivity;
+ ListView mList;
+ VersionsListAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActivity = (MainActivity) getActivity();
+ mActivity.addUpdateListener(this);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View rootView = inflater.inflate(R.layout.fragment_versions, container, false);
+
+ mAdapter = new VersionsListAdapter(mActivity, new ArrayList());
+
+ mList = (ListView) rootView.findViewById(R.id.list);
+ mList.setAdapter(mAdapter);
+ mList.setOnItemClickListener(this);
+
+ return rootView;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mActivity.removeUpdateListener(this);
+ }
+
+ @Override
+ public void onUpdate(Db response) {
+ if (response != null) {
+ mAdapter.clear();
+
+ // get all versions from db
+ List versions = VersionDaoHelper.getVersions(getActivity());
+
+ mAdapter.addAll(versions);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Intent intent = new Intent(mActivity, VersionDisplayActivity.class);
+ intent.putExtra("version", mAdapter.getItem(position));
+ mActivity.startActivityForResult(intent, MainActivity.REQUEST_DISPLAY_VERSION);
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/models/ViewHolder.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/models/ViewHolder.java
new file mode 100644
index 0000000..dbd867d
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/models/ViewHolder.java
@@ -0,0 +1,7 @@
+package com.janibanez.midevtest.models;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public abstract class ViewHolder {
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/DialogUtilities.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/DialogUtilities.java
new file mode 100644
index 0000000..2bdb7b1
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/DialogUtilities.java
@@ -0,0 +1,48 @@
+package com.janibanez.midevtest.utilities;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.text.TextUtils;
+import android.view.Window;
+
+import com.janibanez.midevtest.R;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class DialogUtilities {
+
+ public static void showConfirmation(Context context, String title, String message,
+ DialogInterface.OnClickListener neutralClickListener,
+ DialogInterface.OnClickListener positiveClickListener) {
+
+ AlertDialog dialog = new AlertDialog.Builder(context).create();
+ dialog.setTitle(title);
+ dialog.setMessage(message);
+ dialog.setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(R.string.text_no), neutralClickListener);
+ dialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(R.string.text_yes), positiveClickListener);
+
+ if (TextUtils.isEmpty(title)) {
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ dialog.show();
+ }
+
+ public static void showMessageDialog(Context context, String title, String message,
+ DialogInterface.OnClickListener listener) {
+
+ AlertDialog dialog = new AlertDialog.Builder(context).create();
+ dialog.setTitle(title);
+ dialog.setMessage(message);
+ dialog.setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(R.string.text_close), listener);
+
+ if (TextUtils.isEmpty(title)) {
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ dialog.show();
+ }
+
+}
diff --git a/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/ImageCache.java b/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/ImageCache.java
new file mode 100644
index 0000000..ab16afa
--- /dev/null
+++ b/MIDevTest/app/src/main/java/com/janibanez/midevtest/utilities/ImageCache.java
@@ -0,0 +1,91 @@
+package com.janibanez.midevtest.utilities;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.util.LruCache;
+
+/**
+ * Created by jwgibanez on 22/01/2016.
+ */
+public class ImageCache {
+ private static final String TAG = "ImageCache";
+
+ private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 1024 * 5; // 5MB
+ private LruCache mMemoryCache;
+ private ImageCacheParams mCacheParams;
+
+ public ImageCache(ImageCacheParams cacheParams) {
+ init(cacheParams);
+ }
+
+ private void init(ImageCacheParams cacheParams) {
+ mCacheParams = cacheParams;
+
+ Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
+
+ mMemoryCache = new LruCache(mCacheParams.memCacheSize) {
+ @Override
+ protected int sizeOf(String key, Bitmap bitmap) {
+ return getBitmapSize(bitmap);
+ }
+ };
+ }
+
+ public void addBitmapToMemCache(String data, Bitmap bitmap) {
+ if (data == null || bitmap == null) {
+ return;
+ }
+
+ // Add to memory cache
+ if (mMemoryCache != null && mMemoryCache.get(data) == null) {
+ mMemoryCache.put(data, bitmap);
+ }
+ }
+
+ public Bitmap getBitmapFromMemCache(String data) {
+ Bitmap bitmap = null;
+ if (mMemoryCache != null) {
+ bitmap = mMemoryCache.get(data);
+ }
+
+ if (bitmap != null && !bitmap.isRecycled())
+ return bitmap;
+
+ return null;
+ }
+
+ public void clearCache() {
+ if (mMemoryCache != null) {
+ mMemoryCache.evictAll();
+ Log.d(TAG, "Memory cache cleared");
+ }
+ }
+
+ public void removeCache(String data) {
+ if (mMemoryCache != null && mMemoryCache.get(data) != null)
+ mMemoryCache.remove(data);
+ }
+
+ public static class ImageCacheParams {
+ public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
+ public String diskCachePath;
+
+ public ImageCacheParams(Context context, float percent, String diskCachePath) {
+ this.diskCachePath = diskCachePath;
+ setMemCacheSizePercent(context, percent);
+ }
+
+ public void setMemCacheSizePercent(Context context, float percent) {
+ if (percent < 0.05f || percent > 0.8f) {
+ throw new IllegalArgumentException("setMemCacheSizePercent - percent must be "
+ + "between 0.05 and 0.8 (inclusive)");
+ }
+ this.memCacheSize = Math.round(percent * Runtime.getRuntime().maxMemory());
+ }
+ }
+
+ public static int getBitmapSize(Bitmap bitmap) {
+ return bitmap.getByteCount();
+ }
+}
diff --git a/MIDevTest/app/src/main/res/layout/activity_display_device.xml b/MIDevTest/app/src/main/res/layout/activity_display_device.xml
new file mode 100644
index 0000000..37d4cdf
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/activity_display_device.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MIDevTest/app/src/main/res/layout/activity_display_version.xml b/MIDevTest/app/src/main/res/layout/activity_display_version.xml
new file mode 100644
index 0000000..bfcaa6b
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/activity_display_version.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/activity_edit_device.xml b/MIDevTest/app/src/main/res/layout/activity_edit_device.xml
new file mode 100644
index 0000000..5a1b436
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/activity_edit_device.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MIDevTest/app/src/main/res/layout/activity_edit_version.xml b/MIDevTest/app/src/main/res/layout/activity_edit_version.xml
new file mode 100644
index 0000000..dbf6dc6
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/activity_edit_version.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/activity_main.xml b/MIDevTest/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..b7f0db8
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/content_main.xml b/MIDevTest/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..7cab0b0
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/MIDevTest/app/src/main/res/layout/fragment_devices.xml b/MIDevTest/app/src/main/res/layout/fragment_devices.xml
new file mode 100644
index 0000000..0fab157
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/fragment_devices.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/fragment_versions.xml b/MIDevTest/app/src/main/res/layout/fragment_versions.xml
new file mode 100644
index 0000000..0fab157
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/fragment_versions.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/list_item_device.xml b/MIDevTest/app/src/main/res/layout/list_item_device.xml
new file mode 100644
index 0000000..2684c0a
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/list_item_device.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/layout/list_item_version.xml b/MIDevTest/app/src/main/res/layout/list_item_version.xml
new file mode 100644
index 0000000..b41c33c
--- /dev/null
+++ b/MIDevTest/app/src/main/res/layout/list_item_version.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/menu/menu_display_device.xml b/MIDevTest/app/src/main/res/menu/menu_display_device.xml
new file mode 100644
index 0000000..5f3c6c1
--- /dev/null
+++ b/MIDevTest/app/src/main/res/menu/menu_display_device.xml
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/menu/menu_display_version.xml b/MIDevTest/app/src/main/res/menu/menu_display_version.xml
new file mode 100644
index 0000000..5f3c6c1
--- /dev/null
+++ b/MIDevTest/app/src/main/res/menu/menu_display_version.xml
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/menu/menu_edit_device.xml b/MIDevTest/app/src/main/res/menu/menu_edit_device.xml
new file mode 100644
index 0000000..436d123
--- /dev/null
+++ b/MIDevTest/app/src/main/res/menu/menu_edit_device.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/menu/menu_edit_version.xml b/MIDevTest/app/src/main/res/menu/menu_edit_version.xml
new file mode 100644
index 0000000..436d123
--- /dev/null
+++ b/MIDevTest/app/src/main/res/menu/menu_edit_version.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/MIDevTest/app/src/main/res/menu/menu_main.xml b/MIDevTest/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..2222189
--- /dev/null
+++ b/MIDevTest/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,29 @@
+
diff --git a/MIDevTest/app/src/main/res/mipmap-hdpi/ic_launcher.png b/MIDevTest/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/MIDevTest/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MIDevTest/app/src/main/res/mipmap-mdpi/ic_launcher.png b/MIDevTest/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/MIDevTest/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MIDevTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/MIDevTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/MIDevTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MIDevTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MIDevTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/MIDevTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MIDevTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MIDevTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/MIDevTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MIDevTest/app/src/main/res/values-v21/styles.xml b/MIDevTest/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..251fb9f
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,9 @@
+>
+
+
+
diff --git a/MIDevTest/app/src/main/res/values-w820dp/dimens.xml b/MIDevTest/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/MIDevTest/app/src/main/res/values/colors.xml b/MIDevTest/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/MIDevTest/app/src/main/res/values/dimens.xml b/MIDevTest/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..812cb7b
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 16dp
+ 16dp
+ 16dp
+
diff --git a/MIDevTest/app/src/main/res/values/strings.xml b/MIDevTest/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7ea60fe
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+ MI Dev Test
+ com.janibanez.midevtest.db.provider.DBContentProvider
+ Add
+ Close
+ Create
+ Delete
+ Edit
+ Add Device
+ Add Version
+ Settings
+ Alert
+ Are you sure you want to delete this device?
+ Are you sure you want to delete this version?
+ Oops! Something went wrong with your request. Please try again.
+ Cancel
+ Close
+ Delete
+ Device
+ No
+ Yes
+ Version
+
diff --git a/MIDevTest/app/src/main/res/values/styles.xml b/MIDevTest/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..545b9c6
--- /dev/null
+++ b/MIDevTest/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MIDevTest/app/src/test/java/com/janibanez/midevtest/ExampleUnitTest.java b/MIDevTest/app/src/test/java/com/janibanez/midevtest/ExampleUnitTest.java
new file mode 100644
index 0000000..f2e3662
--- /dev/null
+++ b/MIDevTest/app/src/test/java/com/janibanez/midevtest/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package com.janibanez.midevtest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/build.gradle b/MIDevTest/build.gradle
new file mode 100644
index 0000000..e0b366a
--- /dev/null
+++ b/MIDevTest/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.5.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/MIDevTest/database/.gitignore b/MIDevTest/database/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MIDevTest/database/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MIDevTest/database/build.gradle b/MIDevTest/database/build.gradle
new file mode 100644
index 0000000..55498c3
--- /dev/null
+++ b/MIDevTest/database/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile project(':server')
+}
diff --git a/MIDevTest/database/proguard-rules.pro b/MIDevTest/database/proguard-rules.pro
new file mode 100644
index 0000000..d9fbada
--- /dev/null
+++ b/MIDevTest/database/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/jwgibanez/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/MIDevTest/database/src/androidTest/java/com/janibanez/database/ApplicationTest.java b/MIDevTest/database/src/androidTest/java/com/janibanez/database/ApplicationTest.java
new file mode 100644
index 0000000..f5fb0ae
--- /dev/null
+++ b/MIDevTest/database/src/androidTest/java/com/janibanez/database/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.janibanez.database;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/database/src/main/AndroidManifest.xml b/MIDevTest/database/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1dac48e
--- /dev/null
+++ b/MIDevTest/database/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/DbContenProviderBase.java b/MIDevTest/database/src/main/java/com/janibanez/database/DbContenProviderBase.java
new file mode 100644
index 0000000..2df3fc5
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/DbContenProviderBase.java
@@ -0,0 +1,104 @@
+package com.janibanez.database;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public abstract class DbContenProviderBase extends ContentProvider {
+
+ private final String mTAG = "DbContenProviderBase";
+
+ private static String CONTENT_PROVIDER_AUTHORITY = "";
+ private static String CONTENT_URL = "";
+
+ private static final String
+ CONTENT_DEVICE = "device",
+ CONTENT_VERSION = "version";
+
+ private static void initContentProvider(Context context) {
+ if(CONTENT_PROVIDER_AUTHORITY.equals("")) {
+ CONTENT_PROVIDER_AUTHORITY = context.getResources().getString(R.string.db_content_provider);
+ }
+
+ if(CONTENT_URL.equals("")) {
+ CONTENT_URL = "content://" + CONTENT_PROVIDER_AUTHORITY + "/";
+ }
+ }
+
+ // content provider modules
+ public static Uri CONTENT_URI_DEVICE(Context context) {
+ initContentProvider(context);
+ return Uri.parse(CONTENT_URL + CONTENT_DEVICE);
+ }
+
+ public static Uri CONTENT_URI_VERSION(Context context) {
+ initContentProvider(context);
+ return Uri.parse(CONTENT_URL + CONTENT_VERSION);
+ }
+
+ // content provider sub-modules
+ public static final int
+ URI_DEVICE = 100,
+ URI_DEVICE_ITEM = 101,
+ URI_VERSION = 200,
+ URI_VERSION_ITEM = 201;
+
+ public static final String METHOD_EXEC_SQL = "execSQL";
+
+ public static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+ public static SQLiteDatabase mDatabase;
+
+ private void initUriMatcher(Context context) {
+ initContentProvider(context);
+ mUriMatcher.addURI(CONTENT_PROVIDER_AUTHORITY, CONTENT_DEVICE, URI_DEVICE);
+ mUriMatcher.addURI(CONTENT_PROVIDER_AUTHORITY, CONTENT_DEVICE + "/#", URI_DEVICE_ITEM);
+ mUriMatcher.addURI(CONTENT_PROVIDER_AUTHORITY, CONTENT_VERSION, URI_VERSION);
+ mUriMatcher.addURI(CONTENT_PROVIDER_AUTHORITY, CONTENT_DEVICE + "/#", URI_VERSION_ITEM);
+ }
+
+ @Override
+ public boolean onCreate() {
+ Context context = getContext().getApplicationContext();
+
+ DbHelper databaseHelper = new DbHelper(context);
+
+ initUriMatcher(context);
+
+ try {
+ mDatabase = databaseHelper.getWritableDatabase();
+ } catch (Exception e) {
+ Log.e(mTAG, "Failed to create content provider!", e);
+ }
+
+ return mDatabase != null;
+ }
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ if (METHOD_EXEC_SQL.equals(method) && arg != null) {
+ try {
+ mDatabase.beginTransaction();
+ mDatabase.execSQL(arg);
+ mDatabase.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ return (null);
+ }
+
+ public static SQLiteDatabase getDatabase() {
+ return mDatabase;
+ }
+}
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/DbContentProvider.java b/MIDevTest/database/src/main/java/com/janibanez/database/DbContentProvider.java
new file mode 100644
index 0000000..92a3acd
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/DbContentProvider.java
@@ -0,0 +1,125 @@
+package com.janibanez.database;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+import com.janibanez.database.models.DbDevice;
+import com.janibanez.database.models.DbVersion;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DbContentProvider extends DbContenProviderBase {
+
+ private final String mTAG = "DbContentProvider";
+
+ // cursor to contain 0..x items
+ private final String MIME_TYPE_ALL = "vnd.android.cursor.dir";
+
+ // cursor to contain 1 item
+ private final String MIME_TYPE_ITEM = "vnd.android.cursor.item";
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ switch (mUriMatcher.match(uri)) {
+ case URI_DEVICE:
+ DbDevice.deleteDevice(mDatabase, selection, selectionArgs);
+ break;
+ case URI_VERSION:
+ DbVersion.deleteVersion(mDatabase, selection, selectionArgs);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported URI: " + uri);
+ }
+
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ return 0;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ Cursor cursor;
+
+ switch (mUriMatcher.match(uri)) {
+ case URI_DEVICE:
+ cursor = DbDevice.getDevice(mDatabase, selection, selectionArgs);
+ break;
+ case URI_DEVICE_ITEM:
+ cursor = DbDevice.getDevice(mDatabase, selection, selectionArgs);
+ break;
+ case URI_VERSION:
+ cursor = DbVersion.getVersion(mDatabase, selection, selectionArgs);
+ break;
+ case URI_VERSION_ITEM:
+ cursor = DbVersion.getVersion(mDatabase, selection, selectionArgs);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported URI: " + uri);
+ }
+
+ if (cursor != null) cursor.setNotificationUri(getContext().getContentResolver(), uri);
+
+ return cursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ switch (mUriMatcher.match(uri)) {
+ case URI_DEVICE:
+ return MIME_TYPE_ALL + "/" + CONTENT_URI_DEVICE(getContext()).getLastPathSegment();
+ case URI_DEVICE_ITEM:
+ return MIME_TYPE_ITEM + "/" + CONTENT_URI_DEVICE(getContext()).getLastPathSegment();
+ case URI_VERSION:
+ return MIME_TYPE_ALL + "/" + CONTENT_URI_VERSION(getContext()).getLastPathSegment();
+ case URI_VERSION_ITEM:
+ return MIME_TYPE_ITEM + "/" + CONTENT_URI_DEVICE(getContext()).getLastPathSegment();
+ default:
+ throw new IllegalArgumentException("Unsupported URI: " + uri);
+ }
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ long returnId;
+
+ switch (mUriMatcher.match(uri)) {
+ case URI_DEVICE:
+ returnId = DbDevice.insertDevice(mDatabase, values);
+ break;
+ case URI_VERSION:
+ returnId = DbVersion.insertVersion(mDatabase, values);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported URI: " + uri);
+ }
+
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ Uri returnUri = ContentUris.withAppendedId(uri, returnId);
+
+ return returnUri;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ int count;
+
+ switch (mUriMatcher.match(uri)) {
+ case URI_DEVICE:
+ count = DbDevice.updateDevice(mDatabase, values, selection, selectionArgs);
+ break;
+ case URI_VERSION:
+ count = DbVersion.updateVersion(mDatabase, values, selection, selectionArgs);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported URI: " + uri);
+ }
+
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ return count;
+ }
+}
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/DbHelper.java b/MIDevTest/database/src/main/java/com/janibanez/database/DbHelper.java
new file mode 100644
index 0000000..65804b3
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/DbHelper.java
@@ -0,0 +1,37 @@
+package com.janibanez.database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.janibanez.database.models.DbDevice;
+import com.janibanez.database.models.DbVersion;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DbHelper extends SQLiteOpenHelper {
+
+ public static final String DATABASE_NAME = "MiDevTestDb";
+ public static final int DATABASE_VERSION = 1;
+
+ DbHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase sqLiteDatabase) {
+ sqLiteDatabase.execSQL(DbDevice.TABLE_CREATE);
+ sqLiteDatabase.execSQL(DbVersion.TABLE_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
+ //if (newVersion > oldVersion) {
+ /* starting upgrade */
+ //if (oldVersion < 2 && newVersion >= 2) {
+ // upgradeToV2(sqLiteDatabase);
+ //}
+ //}
+ }
+}
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/helpers/DeviceDaoHelper.java b/MIDevTest/database/src/main/java/com/janibanez/database/helpers/DeviceDaoHelper.java
new file mode 100644
index 0000000..d38531c
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/helpers/DeviceDaoHelper.java
@@ -0,0 +1,68 @@
+package com.janibanez.database.helpers;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+
+import com.janibanez.database.DbContentProvider;
+import com.janibanez.database.models.DbDevice;
+import com.janibanez.server.models.Device;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DeviceDaoHelper {
+
+ public static void insertDevice(Context context, ContentValues values) {
+ String selection = DbDevice.COLUMN_ID + " = ?";
+ String[] selectionArgs = {values.getAsString(DbDevice.COLUMN_ID)};
+
+ Cursor cursor = context.getContentResolver().query(DbContentProvider.CONTENT_URI_DEVICE(context), null, selection, selectionArgs, null);
+
+ if (cursor != null) {
+ int count = cursor.getCount();
+ cursor.close();
+ if (count > 0) {
+ context.getContentResolver().update(DbContentProvider.CONTENT_URI_DEVICE(context), values, selection, selectionArgs);
+
+ }
+ }
+
+ context.getContentResolver().insert(DbContentProvider.CONTENT_URI_DEVICE(context), values);
+ }
+
+ public static List getDevices(Context context) {
+ List models = new ArrayList<>();
+
+ Cursor cursor = context.getContentResolver().query(DbContentProvider.CONTENT_URI_DEVICE(context), null, null, null, null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Device model = new Device();
+ model.id = cursor.getInt(cursor.getColumnIndex(DbDevice.COLUMN_ID));
+ model.androidId = cursor.getString(cursor.getColumnIndex(DbDevice.COLUMN_ANDROID_ID));
+ model.name = cursor.getString(cursor.getColumnIndex(DbDevice.COLUMN_NAME));
+ model.snippet = cursor.getString(cursor.getColumnIndex(DbDevice.COLUMN_SNIPPET));
+ model.carrier = cursor.getString(cursor.getColumnIndex(DbDevice.COLUMN_CARRIER));
+ model.imageUrl = cursor.getString(cursor.getColumnIndex(DbDevice.COLUMN_IMAGE_URL));
+ models.add(model);
+ }
+ cursor.close();
+ }
+
+ return models;
+ }
+
+ public static boolean deleteDevices(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+
+ int count = contentResolver.delete(DbContentProvider.CONTENT_URI_DEVICE(context), null, null);
+
+ return (count > 0);
+ }
+
+}
\ No newline at end of file
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/helpers/VersionDaoHelper.java b/MIDevTest/database/src/main/java/com/janibanez/database/helpers/VersionDaoHelper.java
new file mode 100644
index 0000000..73fd22c
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/helpers/VersionDaoHelper.java
@@ -0,0 +1,67 @@
+package com.janibanez.database.helpers;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+
+import com.janibanez.database.DbContentProvider;
+import com.janibanez.database.models.DbVersion;
+import com.janibanez.server.models.Version;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class VersionDaoHelper {
+
+ public static void insertVersion(Context context, ContentValues values) {
+ String selection = DbVersion.COLUMN_ID + " = ?";
+ String[] selectionArgs = { values.getAsString(DbVersion.COLUMN_ID) };
+
+ Cursor cursor = context.getContentResolver().query(DbContentProvider.CONTENT_URI_VERSION(context), null, selection, selectionArgs, null);
+
+ if (cursor != null) {
+ int count = cursor.getCount();
+ cursor.close();
+ if (count > 0) {
+ context.getContentResolver().update(DbContentProvider.CONTENT_URI_VERSION(context), values, selection, selectionArgs);
+
+ }
+ }
+
+ context.getContentResolver().insert(DbContentProvider.CONTENT_URI_VERSION(context), values);
+ }
+
+ public static List getVersions(Context context) {
+ List models = new ArrayList<>();
+
+ Cursor cursor = context.getContentResolver().query(DbContentProvider.CONTENT_URI_VERSION(context), null, null, null, null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Version model = new Version();
+ model.id = cursor.getInt(cursor.getColumnIndex(DbVersion.COLUMN_ID));
+ model.version = cursor.getString(cursor.getColumnIndex(DbVersion.COLUMN_VERSION));
+ model.name = cursor.getString(cursor.getColumnIndex(DbVersion.COLUMN_NAME));
+ model.codename = cursor.getString(cursor.getColumnIndex(DbVersion.COLUMN_CODENAME));
+ model.target = cursor.getString(cursor.getColumnIndex(DbVersion.COLUMN_TARGET));
+ model.distribution = cursor.getString(cursor.getColumnIndex(DbVersion.COLUMN_DISTRIBUTION));
+ models.add(model);
+ }
+ cursor.close();
+ }
+
+ return models;
+ }
+
+ public static boolean deleteVersions(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+
+ int count = contentResolver.delete(DbContentProvider.CONTENT_URI_VERSION(context), null, null);
+
+ return (count > 0);
+ }
+}
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/models/DbDevice.java b/MIDevTest/database/src/main/java/com/janibanez/database/models/DbDevice.java
new file mode 100644
index 0000000..8408d90
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/models/DbDevice.java
@@ -0,0 +1,92 @@
+package com.janibanez.database.models;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DbDevice {
+
+ public static final String
+ TABLE_NAME = "Device",
+ COLUMN_ID = "_Id",
+ COLUMN_NAME = "Name",
+ COLUMN_ANDROID_ID = "AndroidId",
+ COLUMN_CARRIER = "Carrier",
+ COLUMN_SNIPPET = "Snippet",
+ COLUMN_IMAGE_URL = "ImageUrl";
+
+ public static final String
+ TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " ( " +
+ COLUMN_ID + " INTEGER PRIMARY KEY NOT NULL, " +
+ COLUMN_NAME + " VARCHAR, " +
+ COLUMN_ANDROID_ID + " VARCHAR, " +
+ COLUMN_CARRIER + " VARCHAR, " +
+ COLUMN_SNIPPET + " VARCHAR, " +
+ COLUMN_IMAGE_URL + " VARCHAR )";
+
+ public static final String[]
+ COLUMNS_ALL = {
+ COLUMN_ID,
+ COLUMN_NAME,
+ COLUMN_ANDROID_ID,
+ COLUMN_CARRIER,
+ COLUMN_SNIPPET,
+ COLUMN_IMAGE_URL
+ };
+
+ public static Cursor getDevice(SQLiteDatabase db, String selection, String[] selectionArgs) {
+ Cursor cursor = null;
+ try {
+ String[] columns = COLUMNS_ALL;
+ String sortOrder = COLUMN_ID + " ASC";
+ cursor = db.query(TABLE_NAME, columns, selection, selectionArgs, null, null, sortOrder);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return cursor;
+ }
+
+ public static long insertDevice(SQLiteDatabase db, ContentValues values) {
+ long returnId = 0;
+ try {
+ db.beginTransaction();
+ returnId = db.insert(TABLE_NAME, null, values);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ return returnId;
+ }
+
+ public static int updateDevice(SQLiteDatabase db, ContentValues values, String selection, String[] selectionArgs) {
+ int count = 0;
+ try {
+ db.beginTransaction();
+ count = db.update(TABLE_NAME, values, selection, selectionArgs);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ return count;
+ }
+
+ public static void deleteDevice(SQLiteDatabase db, String selection, String[] selectionArgs) {
+ try {
+ db.beginTransaction();
+ db.delete(TABLE_NAME, selection, selectionArgs);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+}
diff --git a/MIDevTest/database/src/main/java/com/janibanez/database/models/DbVersion.java b/MIDevTest/database/src/main/java/com/janibanez/database/models/DbVersion.java
new file mode 100644
index 0000000..c549e89
--- /dev/null
+++ b/MIDevTest/database/src/main/java/com/janibanez/database/models/DbVersion.java
@@ -0,0 +1,91 @@
+package com.janibanez.database.models;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public class DbVersion {
+
+ public static final String
+ TABLE_NAME = "Version",
+ COLUMN_ID = "_Id",
+ COLUMN_NAME = "Name",
+ COLUMN_CODENAME = "Codename",
+ COLUMN_VERSION = "Version",
+ COLUMN_TARGET = "Target",
+ COLUMN_DISTRIBUTION = "Distribution";
+
+ public static final String
+ TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " ( " +
+ COLUMN_ID + " INTEGER PRIMARY KEY NOT NULL, " +
+ COLUMN_NAME + " VARCHAR, " +
+ COLUMN_CODENAME + " VARCHAR, " +
+ COLUMN_VERSION + " VARCHAR, " +
+ COLUMN_TARGET + " VARCHAR, " +
+ COLUMN_DISTRIBUTION + " VARCHAR )";
+
+ public static final String[]
+ COLUMNS_ALL = {
+ COLUMN_ID,
+ COLUMN_NAME,
+ COLUMN_CODENAME,
+ COLUMN_VERSION,
+ COLUMN_TARGET,
+ COLUMN_DISTRIBUTION
+ };
+
+ public static Cursor getVersion(SQLiteDatabase db, String selection, String[] selectionArgs) {
+ Cursor cursor = null;
+ try {
+ String[] columns = COLUMNS_ALL;
+ String sortOrder = COLUMN_ID + " ASC";
+ cursor = db.query(TABLE_NAME, columns, selection, selectionArgs, null, null, sortOrder);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return cursor;
+ }
+
+ public static long insertVersion(SQLiteDatabase db, ContentValues values) {
+ long returnId = 0;
+ try {
+ db.beginTransaction();
+ returnId = db.insert(TABLE_NAME, null, values);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ return returnId;
+ }
+
+ public static int updateVersion(SQLiteDatabase db, ContentValues values, String selection, String[] selectionArgs) {
+ int count = 0;
+ try {
+ db.beginTransaction();
+ count = db.update(TABLE_NAME, values, selection, selectionArgs);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ return count;
+ }
+
+ public static void deleteVersion(SQLiteDatabase db, String selection, String[] selectionArgs) {
+ try {
+ db.beginTransaction();
+ db.delete(TABLE_NAME, selection, selectionArgs);
+ db.setTransactionSuccessful();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ db.endTransaction();
+ }
+ }
+}
diff --git a/MIDevTest/database/src/main/res/values/strings.xml b/MIDevTest/database/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a507279
--- /dev/null
+++ b/MIDevTest/database/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ database
+ com.janibanez.midevtest.db.provider.DBContentProvider
+
diff --git a/MIDevTest/database/src/test/java/com/janibanez/database/ExampleUnitTest.java b/MIDevTest/database/src/test/java/com/janibanez/database/ExampleUnitTest.java
new file mode 100644
index 0000000..c150bdf
--- /dev/null
+++ b/MIDevTest/database/src/test/java/com/janibanez/database/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package com.janibanez.database;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/gradle.properties b/MIDevTest/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/MIDevTest/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/MIDevTest/gradle/wrapper/gradle-wrapper.jar b/MIDevTest/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..05ef575
Binary files /dev/null and b/MIDevTest/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MIDevTest/gradle/wrapper/gradle-wrapper.properties b/MIDevTest/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f23df6e
--- /dev/null
+++ b/MIDevTest/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Oct 21 11:34:03 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/MIDevTest/gradlew b/MIDevTest/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/MIDevTest/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/MIDevTest/gradlew.bat b/MIDevTest/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/MIDevTest/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/MIDevTest/server/.gitignore b/MIDevTest/server/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/MIDevTest/server/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/MIDevTest/server/build.gradle b/MIDevTest/server/build.gradle
new file mode 100644
index 0000000..6fac4b1
--- /dev/null
+++ b/MIDevTest/server/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.1"
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.squareup.okhttp3:okhttp:3.0.1'
+ compile 'com.google.code.gson:gson:2.5'
+}
diff --git a/MIDevTest/server/proguard-rules.pro b/MIDevTest/server/proguard-rules.pro
new file mode 100644
index 0000000..d9fbada
--- /dev/null
+++ b/MIDevTest/server/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/jwgibanez/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/MIDevTest/server/src/androidTest/java/com/janibanez/server/ApplicationTest.java b/MIDevTest/server/src/androidTest/java/com/janibanez/server/ApplicationTest.java
new file mode 100644
index 0000000..a4207d7
--- /dev/null
+++ b/MIDevTest/server/src/androidTest/java/com/janibanez/server/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.janibanez.server;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/server/src/main/AndroidManifest.xml b/MIDevTest/server/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e63e730
--- /dev/null
+++ b/MIDevTest/server/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/ICallback.java b/MIDevTest/server/src/main/java/com/janibanez/server/ICallback.java
new file mode 100644
index 0000000..d93c8ba
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/ICallback.java
@@ -0,0 +1,13 @@
+package com.janibanez.server;
+
+import java.io.IOException;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public interface ICallback {
+
+ void onFailure(Throwable throwable);
+ void onResponse(T response) throws IOException;
+
+}
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/MiApi.java b/MIDevTest/server/src/main/java/com/janibanez/server/MiApi.java
new file mode 100644
index 0000000..7c5a481
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/MiApi.java
@@ -0,0 +1,331 @@
+package com.janibanez.server;
+
+import android.content.Context;
+import android.os.Handler;
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.janibanez.server.models.Db;
+import com.janibanez.server.models.Device;
+import com.janibanez.server.models.ServerModel;
+import com.janibanez.server.models.Version;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.FormBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class MiApi {
+
+ private static final String API_URL = "http://mobilesandboxdev.azurewebsites.net";
+
+ private static final String DB = "/db";
+ private static final String DEVICES = "/devices";
+ private static final String VERSIONS = "/android";
+ private static final String DEVICE_RESOURCE = "/devices/%s";
+ private static final String VERSION_RESOURCE = "/android/%s";
+
+ public enum Action {
+ CreateDevice,
+ CreateVersion,
+ GetDb,
+ DeleteDevice,
+ DeleteVersion,
+ UpdateDevice,
+ UpdateVersion
+ }
+
+ Context mContext;
+ Gson mGson;
+ Handler mHandler;
+ OkHttpClient mClient;
+
+ public MiApi(Context context) {
+ mContext = context;
+ mClient = new OkHttpClient();
+ mGson = new Gson();
+ mHandler = new Handler();
+ }
+
+ public void call(Action action, int id, ServerModel model, ICallback callback) {
+ switch (action) {
+ case CreateDevice:
+ createDevice((Device) model, callback);
+ break;
+ case CreateVersion:
+ createVersion((Version) model, callback);
+ break;
+ case GetDb:
+ getDb(callback);
+ break;
+ case DeleteDevice:
+ deleteDevice(id, callback);
+ break;
+ case DeleteVersion:
+ deleteVersion(id, callback);
+ break;
+ case UpdateDevice:
+ updateDevice(id, (Device) model, callback);
+ break;
+ case UpdateVersion:
+ updateVersion(id, (Version) model, callback);
+ break;
+ }
+ }
+
+ private void createDevice(Device device, final ICallback callback) {
+
+ RequestBody requestBody = new FormBody.Builder()
+ .add("name", device.name)
+ .add("androidId", String.valueOf(device.androidId))
+ .add("carrier", device.carrier)
+ .add("imageUrl", device.imageUrl)
+ .add("snippet", device.snippet)
+ .build();
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, DEVICES).toString())
+ .post(requestBody)
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ Device result = mGson.fromJson(response.body().string(), Device.class);
+ callback.onResponse(result);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void createVersion(Version version, final ICallback callback) {
+
+ RequestBody requestBody = new FormBody.Builder()
+ .add("name", version.name)
+ .add("codename", version.codename)
+ .add("version", version.version)
+ .add("target", version.target)
+ .add("distribution", version.distribution)
+ .build();
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, VERSIONS).toString())
+ .post(requestBody)
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ Version result = mGson.fromJson(response.body().string(), Version.class);
+ callback.onResponse(result);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void getDb(final ICallback callback) {
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, DB).toString())
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ String body = response.body().string();
+ Db db = mGson.fromJson(body, Db.class);
+ callback.onResponse(db);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void deleteDevice(int id, final ICallback callback) {
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, String.format(DEVICE_RESOURCE, id)).toString())
+ .delete()
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ callback.onResponse(null);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void deleteVersion(int id, final ICallback callback) {
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, String.format(VERSION_RESOURCE, id)).toString())
+ .delete()
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ callback.onResponse(null);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void updateDevice(int id, Device device, final ICallback callback) {
+
+ RequestBody requestBody = new FormBody.Builder()
+ .add("name", device.name)
+ .add("androidId", String.valueOf(device.androidId))
+ .add("carrier", device.carrier)
+ .add("imageUrl", device.imageUrl)
+ .add("snippet", device.snippet)
+ .build();
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, String.format(DEVICE_RESOURCE, id)).toString())
+ .put(requestBody)
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ Device result = mGson.fromJson(response.body().string(), Device.class);
+ callback.onResponse(result);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+ private void updateVersion(int id, Version version, final ICallback callback) {
+
+ RequestBody requestBody = new FormBody.Builder()
+ .add("name", version.name)
+ .add("codename", version.codename)
+ .add("version", version.version)
+ .add("target", version.target)
+ .add("distribution", version.distribution)
+ .build();
+
+ Request request = new Request.Builder()
+ .url(TextUtils.concat(API_URL, String.format(VERSION_RESOURCE, id)).toString())
+ .put(requestBody)
+ .build();
+
+ Call call = mClient.newCall(request);
+
+ call.enqueue(new Callback() {
+ @Override
+ public void onFailure(Call call, IOException e) {
+ if (callback != null) {
+ callback.onFailure(e);
+ }
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ if (callback != null) {
+ if (response.isSuccessful()) {
+ Version result = mGson.fromJson(response.body().string(), Version.class);
+ callback.onResponse(result);
+ } else {
+ callback.onFailure(new Exception("Response is unsuccessful."));
+ }
+ }
+ }
+ });
+ }
+
+}
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/models/Db.java b/MIDevTest/server/src/main/java/com/janibanez/server/models/Db.java
new file mode 100644
index 0000000..c03da92
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/models/Db.java
@@ -0,0 +1,14 @@
+package com.janibanez.server.models;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class Db extends ServerModel implements Serializable {
+
+ public List android;
+ public List devices;
+
+}
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/models/Device.java b/MIDevTest/server/src/main/java/com/janibanez/server/models/Device.java
new file mode 100644
index 0000000..166fec9
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/models/Device.java
@@ -0,0 +1,25 @@
+package com.janibanez.server.models;
+
+import java.io.Serializable;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class Device extends ServerModel implements Serializable {
+
+ public int id;
+ public String androidId, carrier, imageUrl, name, snippet;
+
+ public Device() {
+
+ }
+
+ public Device(String name, String androidId, String snippet, String carrier, String imageUrl) {
+ this.name = name;
+ this.androidId = androidId;
+ this.snippet = snippet;
+ this.carrier = carrier;
+ this.imageUrl = imageUrl;
+ }
+
+}
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/models/ServerModel.java b/MIDevTest/server/src/main/java/com/janibanez/server/models/ServerModel.java
new file mode 100644
index 0000000..3dfd356
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/models/ServerModel.java
@@ -0,0 +1,7 @@
+package com.janibanez.server.models;
+
+/**
+ * Created by jwgibanez on 23/01/2016.
+ */
+public abstract class ServerModel {
+}
diff --git a/MIDevTest/server/src/main/java/com/janibanez/server/models/Version.java b/MIDevTest/server/src/main/java/com/janibanez/server/models/Version.java
new file mode 100644
index 0000000..2550746
--- /dev/null
+++ b/MIDevTest/server/src/main/java/com/janibanez/server/models/Version.java
@@ -0,0 +1,25 @@
+package com.janibanez.server.models;
+
+import java.io.Serializable;
+
+/**
+ * Created by jwgibanez on 21/01/2016.
+ */
+public class Version extends ServerModel implements Serializable {
+
+ public int id;
+ public String codename, distribution, name, target, version;
+
+ public Version() {
+
+ }
+
+ public Version(String name, String version, String codename, String target, String distribution) {
+ this.name = name;
+ this.version = version;
+ this.codename = codename;
+ this.target = target;
+ this.distribution = distribution;
+ }
+
+}
diff --git a/MIDevTest/server/src/main/res/values/strings.xml b/MIDevTest/server/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a381ffb
--- /dev/null
+++ b/MIDevTest/server/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Server
+
diff --git a/MIDevTest/server/src/test/java/com/janibanez/server/ExampleUnitTest.java b/MIDevTest/server/src/test/java/com/janibanez/server/ExampleUnitTest.java
new file mode 100644
index 0000000..e519906
--- /dev/null
+++ b/MIDevTest/server/src/test/java/com/janibanez/server/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package com.janibanez.server;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/MIDevTest/settings.gradle b/MIDevTest/settings.gradle
new file mode 100644
index 0000000..ead207f
--- /dev/null
+++ b/MIDevTest/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':server', ':database'