Efficient syncing of app data to the App folder in Google Drive - android

I'm developing an app which stores items for the user, and the user has an option to sync with Google Drive. Each item consists of text and image.
My current method is to save a .json and .jpg file for each item internally to the device, and then once onConnected is called from Drive APIs, upload these files to the user's APP folder in Google Drive. Similar with deletion, I would create a flag that an item with name 'foo' has been deleted, then try to see if the files 'foo.jpg' and 'foo.json' exists in Drive, and delete accordingly.
This works for me now, but often I get the app to crash because of googleApiClient errors and I realize that this is not an efficient method. So what I'm wondering is whether any of you guys know a more efficient method to do this?
In the ideal world I would love to have something like this: I save the files for each item to the device internally, and a library tracks these and sync Drive accordingly.
Thanks for advice!

Based on the fact that you are referring to 'onConnected' and 'googleApiClient', I assume that you're using GDAA not the REST Api.
You should re-think the idea of using file names when dealing with GooDrive. They are not unique in GooDrive and the GDAA's latency will sooner or later cause a problem with presence of multiple files with the same name, created by ubiquitous construct:
if file.name not exist
create file.name
The fact that you use APP folder makes debugging even harder since you don't have feedback by looking at the http://drive.google.com.
You should refer to your files by the DriveId (if using only a single Android device) or by the ResourceId (if you intent to send/broadcast the IDs to a different device/app). And if you decide to use the ResourceId, make sure you handle the latency issues caused by delayed GDAA's promotion of objects to the Drive.
This certainly does not fully cover your question, but it is too broad to answer correctly anyway.
Good Luck

Related

Android Scoped Storage and External Storage

I have an app that wants to have the ability to download files locally. A user can download a file (no restrictions to file type) and should be able to save it on the device so it can be used for other purposes. I would also like the user to be able to delete the file from the app (they would know which file is downloaded and which isn't, a ticker would indicate if it's backed up locally). Say it is a pdf file -- the user would want to open it with different apps or edit it if they have the ability to, or just share it via email. Considering we cannot opt out of scoped storage anymore (requirement to target 30), I got a couple of questions.
I've tried to use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()) and also getExternalFilesDirPath(ctx, Environment.DIRECTORY_DOWNLOADS)). While the former is deprecated, it works for me in that the Files app can be used to navigate to the file in question (and possibly open/delete the file). On the other hand, I am not being able to delete this file from my app due to lack of permissions. Obviously, the latter path is unable to be navigated to by another app (or is it? I haven't found a way, hence the question).
The other thing I've considered using is the MediaStore API but I'm struggling to see how this is an improvement over the old ways in terms of function. Disregarding moving back to manual content resolver and cursor usage, how should arbitrary files be sorted? Should I manually sort by mime types and have different methods for saving for specific media types? It sounds exceptionally tedious and counter intuitive. If this is the way, so be it, I will implement it, but it does not sound like the way to go. On the positive side, it at least sounds like a solution due to the content resolver's CRUD abilities.
I'm working on a RN app that uses a 3rd party library for the download paths, which old/new versions, respectively, use different paths (rn fetch blob and rn fetch blob util). Additionally, MediaStore API doesn't have a RN implementation as of right now, so everything would have to be done from scratch, too.
What are my options? In the short term I'm considering disabling the erase feature from the app (at least for now). Anything I am missing and should consider? Thanks in advance.

Can I upload only template files on google play store?

Looking for a suggestion.
I have uploaded an app on playstore. In that app, I have few templates of questionnaire (xml files - stored in resources).
Client modifies some of the templates once in a month or two.
When client modifies the template, I need to sign and upload APK again.
Is there any way out to upload only the template resources and enable app to read them or something?
Like for media players, themes are uploaded as different package or application, but once downloaded they are part of MediaPlayer.
Thank you!
Currently, as you guessed, the only practical approach is to upload a new APK every time there is new content.
Two ways to solve this:
1. The elegant solution:
If you can re-architect your app to communicate with a web service, you can store your templates there. Once a user installs and runs your app, you can have the app contact the service, authenticate if needed, and download the templates. This can be done once on login or every time the app is run - purely a design decision.
The minimum functionality you need from a web service is an online database. One example is Parse, which is going to discontinue service soon. Another is Firebase, which is free and fairly easy to use. You could store your XML templates in Firebase and have your app download them as needed.
This approach gives the developer control of the template content.
2. The super hacky way
Designate some directory in External Storage (this is important) to store these template files in, and have your app periodically scan this directory for templates to use. When there is a new or updated template, the user downloads the template and then copies it to that specific directory.
Obviously, this plan is full of holes - anything can go wrong here: users not knowing how or where to copy what, misformatted files, etc. And your userbase can have wildly different collections of templates and if something goes wrong, you won't know which templates a particular user has.
So there's the easy and the hard way. Obviously, I recommend the latter, if only for stability and sanity reasons...

android - detect changes on web folder

I'm developing an app which reads content loaded on my company's server. How can I detect if new files are loaded into the designated folder online or whether any of the existing files has been overwritten in that folder?
From what I've researched so far,
(1) FileObserver seems to be a likely solution, but can it check on a specific URL?
(2) I've used Json's Request.Method.HEAD on a single file to detect updates, but I currently have close to 500 over files (and growing) in that folder and it seems like a potential resource hog to process the header information every time the app loads.
Anyone has suggestion on what functions to use, or library which can help in this scenario?
Many many thanks!
How can I detect if new files are loaded into the designated folder online or whether any of the existing files has been overwritten in that folder?
Have the server notify your app of changes, such as via GCM.
Or, have the server publish some sort of a changelog that you monitor.
FileObserver seems to be a likely solution, but can it check on a specific URL?
FileObserver is for local files.
I've used Json's Request.Method.HEAD on a single file to detect updates, but I currently have close to 500 over files (and growing) in that folder and it seems like a potential resource hog to process the header information every time the app loads.
Agreed. Your problem is on the server. Your server needs to provide the change details in a more efficient fashion.
Create a small API that will have method smth like
List<String url> getAllChangedFiles(long timestamp) that return all files that had been changed from your date. In this case all time consuming logic (like searching for such files) will be on the server, you even could cache it for some reasons.
GCM is not a perfect solution 'cause there is no guarantee that your push notification will be delivered.

Creating a unique file in Google Drive in android

I'm using the Google Drive Android API (yes I know its still in developer preview), to create a file on Drive that can persist data across all of a user's devices.
https://developers.google.com/drive/android/create-file
So, use the app on your phone:
-Check that the file exists (filter by filename and not in trash)
--If exists then read and update local sqlite with data
--If not exists then create new file, and write data from sqlite. Then request sync.
-When user changes data, open file again, write contents and commit/sync.
I'm finding situations when testing where I run on my phone, and then on my tablet, and there happens to be multiple files created of the same name and my app gets confused when opening. I'm guessing there were duplicates because there was a delay in the sync and the 2nd device didn't find the existing file so created a new one.
So, now I'm thinking, I pull in all files with that name (not marked trash) and merge them, then mark trash on all but one of them and call that the current. This will leave many files in the trash as time passes.
Couple of questions:
Is there anyway to create a file with a unique name, or pull by some unique handle. The duplicates are making things a hassle to use this Drive product.
Is there anyway to delete a file, not mark for trash, but actually delete. After a merge, I don't want to leave hundreds of files marked for trash on someones drive after months of use.
Am I missing something obvious here? All I want to do is continually overwrite a file by the last device to save data and call that the master copy. Then let other devices know that they should refresh when they run. Right now, I have a network tool app, but I'm also creating a game and was hoping to leverage this same mechanism to save/persist game state across devices....at this point I'm not so confident that Drive is the proper means.
As you've discovered title's are not unique in Drive. However, each item in Drive does have a unique identifier. In the Android API, the DriveId is the representation of this. (In the web API its the resource id).
Once you create the file, you can save the DriveId in local preferences so that next time you can just look it up by the ID. This will guarantee that you will always have the same file on the same device.
On other devices, you can first do a query by title to determine if it exists already, and then save off the DriveId if it does.
Google Play Cloud Save is designed for lightweight persisting of save state across multiple devices including conflict resolution and was built originally for games (although any app can use it). This seems like a more likely fit for what you want to do than trying to work with the full file system approach that is Google Drive.

Offering additional media resources (graphics/sounds) as Google Play in-app billed items

Within my Android app that is available via Google Play, I want to offer additional items that can be bought via in-app billing.
The kind of items I want to offer is media content such as graphics and sounds, which would normally go into the res folder of the app.
The problem is that these resources must be protected, of course. In its documentation, Google suggests not to store the content inside of the application package but to obtain a key after the item was bought and then send the key to a remote server where the key is checked and, if successful, the graphics/sounds offered for download to the app.
This sounds good, from the security perspective. But if I do this, I can't use the content as easily as resources can be accessed normally. If the user can get additional background PNGs, for example, I can't use R.drawable.new_background but have to decode the bitmap programatically, right?
So are there any alternatives or best practices for downloading additional media content via in-app billing?
I would say, as everyone who is determined enough can reverse-engineer the code, anyway, why not just store the content inside of the app but do strong checks if the user might use that content at all.
The answer depends somewhat on your specific concerns, which, from your question, might be either 1 or 2 below:
1) I'm concerned that someone will be able to use my resource within my app without paying, or
2) I'm concerned that a user (either one who has paid, or one who has not paid) will remove my resources and use them outside my app.
Another possibility, that does not seem to be indicated by your question (but which others have attempted to address) would be:
3) I'm concerned about my initial APK download being too large due to downloading a large number of resources, only some of which they user will decide to use.
If your only concern is item 1, then you can just store your resources as you normally would, but inside your app, refuse to load / use any resource for which you have not yet received an in-app payment. This is certainly the simplest approach.
If your concern is item 2, then you can just include with your APK an encrypted binary archive in the raw folder, and decode a specific resource from it whenever that resource is authorized (e.g., by payment) for use by the app. As others have noted, this is not really a big deal in terms of processor load, and it does provide some protection from casual theft. Of course, there is always copyright law if you're dealing with one of those "creative plagiarist" oxymorons who breaks your encryption and steals your resources anyway.
If your concern is item 3, then you'll need an external server from which your items can be retrieved.
Google App Engine is a popular choice for hosting when implementing this kind of storage outside the app. You would want to just cache any purchased /downloaded resources in an encrypted archive in your app's own external files folder (as returned by getExternalFilesDir()), then read and decrypt it as in item 2, above. Such files will be automatically deleted when your app is uninstalled.
One of obvious reasons for not storing additional content in the app is your app's app download size. If you are offering audio as additional content it can drammatically increase the size of your application. And users care about it. Besides it makes it easier to publish additional content, since you can do it via your server side/developer console without the need to publish app update. Moreover, if you want to provide high quality graphics, you will be able to serve appropriate version of the image directly to the device without the need of storing all density/screen size versions.
If you are concerned about security you can always use encryption and signatures to access resources and make attackers life much harder by properly obfuscating your code (or even moving security/decryption related code to native side, which will make it faster as well).
Yes, that would create a drawback that you will have to decode them programmatically and there is nothing to do about it. I honestly don't see why it is so big of a deal, on contrary, I think it's quite convenient that you will have more data driven access to resources.
As a summary I dont really think there are any standards for doing this and it depends on your app and content type. If you offer a fixed amout of 5-10 images, then there it is perfectly fine to keep them locally, if it is richer content, more items, heavier resources, then client-server would suit you more.
One solution would definitely be the following approach featuring two apps that share the same user ID. But this is not as elegant as in-app purchases and a bit hacky, obviously.
Create two apps with the same android:sharedUserId in AndroidManifest.xml
Store the paid content in the second app, that is a library app from now on and only contains those images (apart from necessary folders and manifest)
Sign both APKs with the same key
Offer the first app for free, as usual, and make the second app a paid one
In the free app, use the PackageManager to check if the second APK is installed
If yes, use createPackageContext() to create a Context for the second app which is then used to access the second app's resources from the first app
Google Play offers extension packs that can be purchased. These are large extensions that can be downloaded if purchased, and are separate from your app. This is one possibility.
http://developer.android.com/google/play/expansion-files.html
Depending on how secure you want it, and the size of the media, you can encrypt the media with a key, and store it with your app. It will be downloaded and installed with the app, but inaccessible. When the in-app purchase is complete, you can decrypt the contents and offer it to the user. You can either store the key in the source code with the app to decrypt it, or you can verify the purchase on your own server and retrieve the key from your server.

Categories

Resources