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 @@ + + + + + + + +