mwcaisse / androidft Goto Github PK
View Code? Open in Web Editor NEWAndroid File Transfer
License: MIT License
Android File Transfer
License: MIT License
Update the Entities to all have a common parent that they can inherit from
When creating a request the AFTClient is not properly serializing the Date. It is being serialized as "May 9, 2014 4:19:33 PM" despite having a JsonDate serialize that converts it to milliseconds since the epoch.
The modal part of the File Upload is currently complete.
Need to actually implemented the File uploading part it self
http://stackoverflow.com/questions/17336298/knockout-js-fileupload-event
knockout binding for a file upload. might be possible to use
Create proper error handling when a request fails.
Currently if a request fails and the server returns an error message, the requester simply creates an Exception with the HTTP Status and returns it back to the caller.
The request needs to be reshaped to fit the format of Android External Storage.
It seems that there is not the ability to save files anywhere on the android device, especially without root.
Request should be reshaped to have a new field, which corresponds to one of the android directories (ie. DIRECTORY_MUSIC)
Android SDK Saving Files doc:
http://developer.android.com/training/basics/data-storage/files.html
List of Directories:
http://developer.android.com/reference/android/os/Environment.html
Add the environment as a property to the servlet, to enable / disable thymeleaf view caching based upon the environment it is running in.
Disable cache in local
Enable in other environments.
Saving requests from the web ui fails as there are invalid fields.
The knockout view models in the UI add fields to the models that are only used for UI purposes. Howerver the servlet throws an exception when it finds an unrecognized property.
Sol.
When fetching requests, requestStatus field is populated as null.
MyBatis is able to filter by using the RequestStatus enum properly, evident in the get new requests for device not showing devices without the new status.
However when fetching the request itself, the request status is left null, and there is no exception being thrown.
When the application checks to see if google play services exists, GooglePlayServicesUtil prints an error saying The Google Play services resources were not found.
Which seems to mean that they are not being deployed into the apk properly. Even though they are contained in the dex libs.
Most likely an issue with how maven is currently handling apklib.
Downloading Files from the aft-web with spaces in the file name does not work.
The file name is cut off after the space.
The CORS in the aft-web project no longer seems to be working correctly.
When hosting the web site locally, it is able to make requests to both local server and production server.
However when running on the production server, it is able to make requests locally, but not on the production server. Additionally running locally but viewing from android does not work.
I do believe that previously this was configured and working properly..
Null Pointer Exceptions occurs while making a call to fetching a device image
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/aft-servlet] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at com.ricex.aft.servlet.controller.api.DeviceController.getDeviceImage(DeviceController.java:168)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Currently the login is handled by using the default spring security form. Would like to have this implemented without a login form. Allowing users to login by submitting a request to a login controller.
This Controller will have the following options:
This will allow each application to implement their own way of logging in. Especially the Android and Desktop client, where a form login will not work well.
The drop down for selecting the device to create the request for contains all of the devices on the server. Not just the devices that the user owns / has access to.
Each time the android app processes a file, it creates a separate notification for each rather than updating the previous notification like intended.
When viewing / modifying an existing request, the files that are attached to the request do no populate in the files table.
If the device does not have an image, instead of sending a copy of the default image back, redirect to the default device image, so it will only have to be downloaded once.
Going to need to implement some type of handling if the device is not currently connected to the internet.
For example if the device has no internet connection when it is opened, it will not register with PushFile properly or GCM. This might not need that much handling though, the app can just display a message saying it was unable to register due to lack of connection, and have an option in the settings to try again.
The downloading / notifying the server that requests are completed will need much more work. Currently the app makes no note of requests having been complete, and if the transmission of completion to the server fails, then upon next connection, it will receive requests that it has already processed.
It appears as if the Servlet is not decoding the binary data received from files, before saving into the database.
The data stored inside the database is larger, and not the same as the raw bytes, have not verified that it is indeed still base64 encoded, but that is the most likely what is happening.
The device view page does not scale down properly when the page / window is not as wide.
The device information and request table on the right overlaps the image on the left if the page isn't wide enough to display both.
Implement scaling of the image on the left if the page isn't big enough, as the image doesn't need to be that big. Alternatively make the image / tables vertical if the page is too small.
Create a database to store the users who are allowed to log into the web api.
This database can follow the default javax database schema or a custom set of tables and implement a custom UserDetailsService to access the user data. More information on both can be found here:
http://stackoverflow.com/questions/7171460/using-mysql-database-to-authenticate-users-in-spring-security
Upon opening a RequestView, the DeviceComboBoxModel.onSuccess is repeatedly called, im assuming that there is an infinite loop somewhere. It looks like it is also calling sending the request to the server each time as well.
Local client will send a request over to GCM. But GCM is not sending the request to the phone...
When attempting to get the UID for the device, it cannot find android_id and returns -1 instead.
A configuration issue with Maven and Java 1.8.
When setting the java version to 1.8 in the POM file, maven defaults the compiler version in the project to java 1.4 instead.
Work around for now involves manually setting the java version to 1.8 in eclipse
Jenkins is able to build and deploy the resulting WAR file to the tomcat server. However the servlet does not run as it is missing the data-source.xml, which contains the information about the connection to the database. This file is not in the source control for obvious reasons.
Need to figure out how to include it in the Jenkins build, possibly a different way to load in the needed properties to connect to the data base.
178198 [AWT-EventQueue-0] DEBUG com.ricex.aft.client.controller.RequestController - Making the Async call, and registerign the callback controller
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Can't find route parameter name "id"
at com.mashape.unirest.request.HttpRequest.routeParam(HttpRequest.java:66)
at com.mashape.unirest.request.GetRequest.routeParam(GetRequest.java:42)
at com.ricex.aft.client.request.file.FetchFileRequest.constructServerRequest(FetchFileRequest.java:45)
at com.ricex.aft.client.request.AbstractRequest.getServerRequest(AbstractRequest.java:146)
at com.ricex.aft.client.controller.AbstractController.makeAsyncRequest(AbstractController.java:31)
at com.ricex.aft.client.controller.FileController.getFile(FileController.java:55)
at com.ricex.aft.client.view.request.action.DownloadAction.actionPerformed(DownloadAction.java:63)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6527)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
at java.awt.Component.processEvent(Component.java:6292)
at java.awt.Container.processEvent(Container.java:2234)
at java.awt.Component.dispatchEventImpl(Component.java:4883)
at java.awt.Container.dispatchEventImpl(Container.java:2292)
at java.awt.Component.dispatchEvent(Component.java:4705)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
at java.awt.Container.dispatchEventImpl(Container.java:2278)
at java.awt.Window.dispatchEventImpl(Window.java:2739)
at java.awt.Component.dispatchEvent(Component.java:4705)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
at java.awt.EventQueue.access$400(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.awt.EventQueue$3.run(EventQueue.java:691)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:719)
at java.awt.EventQueue$4.run(EventQueue.java:717)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Settings.Secure.ANDROID_ID, the 64 bit integer being used for the device UID, is a 64bit unsigned integer. Java only supports a 64bit signed integer (long).
Storing the UID in a long is not sufficient.
Solution:
Use the HEX string instead of the long. It is shorter anyways so it will use less bandwidth
Add a date parameter to the recent requests, to allow user / caller to filter the requests on a specific date range, not just a range specified in the code
Implement a database logger for the servlet, to easier read and store the logs for the servlet, especially on prod.
If the servlet returns a 500 ISE when the android app makes a request. The exception is not caught anywhere and bubbles up causing the whole application to crash.
Example stack trace below:
09-16 21:05:08.792: W/RestTemplate(21994): POST request for "http://192.168.1.160:8080/aft-servlet/manager/device/register" resulted in 500 (Internal Server Error); invoking error handler
09-16 21:05:08.792: W/dalvikvm(21994): threadid=14: thread exiting with uncaught exception (group=0x4167cce0)
09-16 21:05:08.802: E/AndroidRuntime(21994): FATAL EXCEPTION: AsyncTask #3
09-16 21:05:08.802: E/AndroidRuntime(21994): Process: com.ricex.aft.android, PID: 21994
09-16 21:05:08.802: E/AndroidRuntime(21994): java.lang.RuntimeException: An error occured while executing doInBackground()
09-16 21:05:08.802: E/AndroidRuntime(21994): at android.os.AsyncTask$3.done(AsyncTask.java:300)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.FutureTask.run(FutureTask.java:242)
09-16 21:05:08.802: E/AndroidRuntime(21994): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.lang.Thread.run(Thread.java:841)
09-16 21:05:08.802: E/AndroidRuntime(21994): Caused by: org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error
09-16 21:05:08.802: E/AndroidRuntime(21994): at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
09-16 21:05:08.802: E/AndroidRuntime(21994): at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:524)
09-16 21:05:08.802: E/AndroidRuntime(21994): at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:481)
09-16 21:05:08.802: E/AndroidRuntime(21994): at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:439)
09-16 21:05:08.802: E/AndroidRuntime(21994): at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:317)
09-16 21:05:08.802: E/AndroidRuntime(21994): at com.ricex.aft.android.requester.DeviceRequester.registerDevice(DeviceRequester.java:37)
09-16 21:05:08.802: E/AndroidRuntime(21994): at com.ricex.aft.android.gcm.GCMRegister$2.doInBackground(GCMRegister.java:194)
09-16 21:05:08.802: E/AndroidRuntime(21994): at com.ricex.aft.android.gcm.GCMRegister$2.doInBackground(GCMRegister.java:1)
09-16 21:05:08.802: E/AndroidRuntime(21994): at android.os.AsyncTask$2.call(AsyncTask.java:288)
09-16 21:05:08.802: E/AndroidRuntime(21994): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
09-16 21:05:08.802: E/AndroidRuntime(21994): ... 4 more
09-16 21:05:08.812: W/ActivityManager(626): Force finishing activity com.ricex.aft.android/.PushFile
09-16 21:05:09.072: W/HandlerScheduledExecuto(1691): Task does not implement UiTask. Consider using NamedUiRunnable for eww@4234ce68
Upon changing the config from XML to Java config, The new implementation of the CORS filter does not seem to be properly passing on the request.
Upon loading the page, it asks for authorization, and adds the correct CORS header, and returns status 200. It never sends the request over to the controller.
When the android app downloads the files, they files are not visible by the Gallery App (they were all pictures in my test cases) nor are they visible in the folder when connected to my PC. They are visible in the file manager though.
Upon inspection this is an issue with MTP and emulated storage space. As my phone does not have a physical external storage / sdcard, the external storage is emulated. The files were written to the external storage, but they needed to be parsed by MediaScannerConnection before they are visible. Which can happen manually, upon reboot, and/or periodically.
I rebooted my phone, and the files were visible afterwards, confirming that this is an issue with the emulated storage.
Currently working on the fix.
Details can be found here:
http://stackoverflow.com/questions/10643476/sdcard-content-exist-but-cant-see-them
Implement retry conditions to the GCM device notifier in the servlet.
Currently if sending the notification to GCM fail, it does not retry sending the request, and there are no indication that the request failed other than the log entry.
Implement a way to track and retry requests that have failed to be notified.
Add the ability to delete requests if the user wishes.
Touching the notification in the notification list is not causing the notifications to be deleted.
http://developer.android.com/training/notify-user/managing.html#Removing
When uploading a large file, in this case 1.4MB, the MYSQL server rejects the update, as the packet is too big.
SEVERE: Servlet.service() for servlet [aft] in context with path [/aft-servlet] threw exception [Request processing failed; nested exception is org.springframework.dao.TransientDataAccessResourceException:
; SQL []; Packet for query is too large (4005979 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.; nested exception is com.mysql.jdbc.PacketTooBigException: Packet for query is too large (4005979 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.] with root cause
com.mysql.jdbc.PacketTooBigException: Packet for query is too large (4005979 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
Add support to create multiple requests at the same time on the AFTClient.
This will add a module to the requestView to add a number of files, and a button to upload them, as well as remove. This will give the user greater control over what files are uploaded, and when they are uploaded.
The creation of a request with multiple files will still result in multiple requests, each which a single file. But from the creation UI it can seem as if only one is being created.
There should be a way to cancel a request that has not been processed yet.
Refactor the logic for handling a request that returns 401 Unauthorized to be compatible with the new authentication method.
Implement validation for Devices, Files, and Requests on the servlet. As well as create a validation exception to return to the user to notify them that the request was invalid. Possible incoporate the same into failure to create / update Requests.
Create a Request object for the android requester class to use when making requests.
Will simplify the parameters passed into the makeRequest and processRequestResponse methods.
The onSucess, onFailure, and onCancel call backs are not being called for post requests, specificly CreateFileRequest, and CreateRequestRequest.
Unirest does seem to support call backs for post and put requests..
Implement Roles, Permissions and Groups for users, allowing access control on specific Devices and Requests.
This will allow users to own specific Requests and Devices, as well as allow them to give other users permissions to access their device. There will be no Read/Write specific access, if a user has access to a specific device, they will have both Read and Write access.
All devices and requests will be assigned to an Owner and possibly a Group, which members of a Group can be a User and other Groups.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.