I am programming a feature inside my app that syncs all the users favorites to Google Drive. The whole point of this feature is that all the devices of that user are in-sync.
There are somethings that are good to know:
Is that the app pulls all the favorites from Google Drive at the startup, compares them with the local favorites. If there is a favorite inside the Google Drive folder that isn't there on the local storage, that favorite gets added to the local storage, and vice-versa.
The app uses the Google Drive API.
The problem with the first point above is the way of comparing cloud and local storage. When the user removes a favorite, and the connection is bad, the favorite isn't removed on Google Drive. When launching the app that same favorite gets added again.
The app can't be launched while offline so a favorite can't be removed offline.
I am thinking of a way to make some kind of 'changelog' to see that a favorite is removed or added, that contains the time that it has been added/removed, and somekind of device id. (I am thinking out loud now) A problem of this is that the changelog file gets quite large when using the app for a while (every favorite addition and removal is logged)
The problem that I am facing is not the code, but the way the syncing needs to work and check for favorites. I've tried submitting the favorites themselves to Google Drive, but that takes up to much space (and can't be debugged easily).
Please think out loud (in the comments haha), I am not sure how I would tackle this challenge and I need to be pointed in the correct direction by someone.
Any solution is welcome.
Change Subscriptions
allows your application to receive ChangeEvents even when it is not running. For example your application could show a notification when a file or folder it cares about has changed, even if that change occurs while your application not running.
The following example code shows the logic you might implement to add a change subscription to a file:
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient,
mSelectedFileId);
file.addChangeSubscription(mGoogleApiClient);
Handle change subscriptions events
To handle change subscription events you must create a class that extends DriveEventService and overrides the onChange method. This is where application-specific processing of the ChangeEvent would occur.
public class MyDriveEventService extends DriveEventService {
#Override
public void onChange(ChangeEvent event) {
Log.d(TAG, event.toString());
// Application-specific handling of event.
}
}
Add this to your AndroidManifest.xml file
<application ...>
...
<service android:name=".MyDriveEventService" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.drive.events.HANDLE_EVENT"/>
</intent-filter>
</service>
</application>
Your application will receive a ChangeEvent whenever an file or folder gets updated by another application on the device or the Drive API downloads changes from the server.
You can also try using Push Notifications (Drive Rest API):
The Drive API provides push notifications that let you watch for changes to resources. You can use this feature to improve the performance of your application. It allows you to eliminate the extra network and compute costs involved with polling resources to determine if they have changed. Whenever a watched resource changes, the Drive API notifies your application.
You can read the blog for push notification for more information.
Hope this helps!
Related
I am in the process of writing a Xamarin.Forms line-of-business application.
The app will be targeting UWP and Android.
I have a requirement of being able to store information and pictures taken, in a shared folder on the local storage. This way, multiple users of the same device at different times can resume work-in-progress of the first user.
I am not sure what my options are, as I am unable to write outside of AppData folder (for UWP).
I read about potentially using a Picker and storing the selected folder in the FutureAccessList for UWP, but I am unsure if it will actually work and seems hacky as I will need to come up with a way of doing the same for Android at a later time.
Any ideas/pointers are greatly appreciated!
There is a special ApplicationData.SharedLocalFolder folder that allows you to share app data across user accounts on a PC. Its main limitation is that it requires appropriate Group Policy:
SharedLocalFolder is only available if the device has the appropriate group policy. If the group policy is not enabled, the device administrator must enable it. From Local Group Policy Editor, navigate to Computer Configuration\Administrative Templates\Windows Components\App Package Deployment, then change the setting "Allow a Windows app to share application data between users" to "Enabled."
I feel that the fact that this is not allowed by default is a great obstacle to the usefulness of this API.
There a publisher cache folder, but this solution is not appropriate for you because of documentation says:
Publisher Cache shares data across apps for the current user
So I would probably really go with the picker-based solution you proposed. Offer the user to select a folder to save the data to using the FolderPicker and then store the selected folder to the FutureAccessList. The future access list is reliable and can even track the changes of the selected item (like when the user moves it to a different location). The abstraction of the selection process in a cross-platform manner may be a bit more complicated, but it should be possible to hide it behind a dependency service implementation. My guess will provide an async method that will initialize the target location. On UWP this will check the FutureAccessList if a location was selected previously and if it was not, it will use the FolderPicker to let the user select it and will store it for future user afterward. On Android, it will work in Android specific manner (I am not sure what are the options there). Then the service will have some file manipulation methods that will abstract the platform-specific manipulation with the folder (I think you cannot use the common System.IO namespace, as you cannot directly access the user selected folder outside of the StorageFolder API)
My goal is to have a folder on the user's drive where he can drop files. My Android application should then detect (via polling) that a new file was created/uploaded in this folder on the drive and download it.
So far so good. I tried to do this with the Google Drive Android API, but I was given to understand that with the Google Drive Android API I do only have access to files created by my application. So if the user were to drop a photo in this folder via another device (e.g. desktop computer), I would not even see the file, let alone if it was newly created or not.
I was told that I could use the Google Drive REST API (v3) to extract the changes that happened since I last looked.
Drive.Changes.List request = drive.changes().list(startPageToken);
ChangeList changes = request.execute();
This does actually work. However, I have three problems with this approach:
I do get a list of changes containing all the files that changed on the drive. I do not, however, get a changeset. I do not know exactly what has changed. Was it a rename, creation, move, deletion, content change? I googled and I seem to be the only one wondering about this. How do I know what exactly changed?
As I described I would like to watch a single folder. How can I request the changes for only a single folder?
And finally: my application is using the native Google Drive Android API. So with adding this feature I suddenly need two APIs instead of one, even though I need one of them only for the simple task of polling for changes. What makes it even worse is that the user could in theory log into one API with one account, and into the other with another account. This does not make any sense. I get a lot of additional overhead and a critical functionality depends on the user logging in with the same account twice. Is there a solution to this problem?
I find it very frustrating that there are two Google Drive APIs avilable to begin with. You have to dig quite a bit to even get started and discover the exact difference if you know nothing initially. I still do not get the difference or why one of them is not capable to see files not created by itself.
You can try checking the documentation about Listening for Change Events:
You can use change listeners to receive notifications whenever a specified file or folder has changes to its contents or metadata. A change listener implements the ChangeListener interface for the ChangeEvent and receives a direct callback from the Drive service to a currently connected client application.
The following example code shows the logic you might implement to add a change listener to a file:
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient,
mSelectedFileId);
file.addChangeListener(mGoogleApiClient, changeListener);
In addition to adding the listener to the file or folder you wish to receive events for, your application must also implement the callback (changeListener in this example) you passed in the addChangeListener method call. The following demonstrates a callback that prints the received events to a log.
/**
* A listener to handle file change events.
*/
final private ChangeListener changeListener = new ChangeListener() {
#Override
public void onChange(ChangeEvent event) {
mLogTextView.setText(String.format("File change event: %s", event));
}
};
For callbacks that take action, such as retrieving the latest version, you can use the hasContentChanged and hasMetadataChanged methods to determine whether the file contents, metadata, or both have changed.
For a full working example, see the ListenChangeEventsForFilesActivity sample in the Google Drive Android API Demos app.
You can also check this related so question 22980497 for additional information.
Hope it helps!
I have a requirement, wherein I have 4 android apps, which are sending notifications to a user, at a fixed time of the day. A user could have one, or more of these apps installed on his phone.
I want only one of the apps(any one) to show this notification to the user, because multiple apps popping up notifications is a bad user experience.
For this I need to share some data across the apps.
I was thinking of a mutex/lock based approach, the problem is, where do I store it?
Problems:
Shared Prefs: I don't know which app wrote the data first, and from which app's context should I read.
SQLite: Same Problem as above and app uninstalls need to be handled and SD card might be missing
Server: Mostly offline app, dont want to add internet permission just for this
I see files at a common location as the only way to store this information.
Is there any better way
As you said that the easiest ways is with file,
I did this before and i too wasn't able to find more easy way.
when you show the notification first time then Just make a new file anywhere which can be common for any app and then check if file is exist.
if it exist then don't show the notification and if not then show and make file again, also remember to delete the file from any of your app when day changed, use AlarmManager for this.
hope it helps.
Hi I'm using the Google Drive Api to store a database using the AppDataFolder facility. I have a test app running successfully on one device. I am able to upload/update/delete/download the database file and reintegrate it into the program with no problems.
The issue I'm having is when I want to share this database with another device running the same app, then things don't work as expected. Example device A I upload the database, device B - I want to download the database but no file is found (this delay can vary greatly from seconds to hours). Reason for this - when using the Api it decides when it wants to 'sync' data, as it is queued rather than being instantaneously uploaded. So when used on one device this is not a problem because it takes either the 'synced' file from the cloud storage, or file waiting to be synced.
I have tried various things like trying to list all AppDataFolder files or retrieving metadata through a query with searchable filters before making a request to update/delete etc.. However I can't get it to work as desired fundamentally it chooses when to sync.
So my actual question is: How can I force Google Drive to sync my file when I want it to i.e. every time a request is made, so that synchronisation is achieved across multiple devices using the same app. There must be an answer as I would think this is quite a fundamental reason why you would use the AppDataFolder is the first place.
Thanks in advance
EDIT/UPDATE:
I have been able to find an option in the Api to 'sync' the drive content using this code:
// try to sync
Drive.DriveApi.requestSync(mGoogleApiClient).setResultCallback(new ResultCallback<com.google.android.gms.common.api.Status>() {
#Override
public void onResult(com.google.android.gms.common.api.Status status) {
if (!status.getStatus().isSuccess()) {
Log.e("SYNCING", "ERROR" + status.getStatusMessage());
} else {
Log.e("SYNCING", "SUCCESS");
// execute async task to list AppFolderContents
new AppFolderContentsAsyncTask(getActivity()).execute();
}
}
});
This works well for 3/4 attempts in quick succession, however I reach a syncing limit and status message:
ERRORSync request rate limit exceeded.
Is there any way to increase the request rate as this is not really desirable if I have to have a app that prompts the user 'please try again later to sync - not sure how long you'll have to wait until you can though!'
SOLUTION - OF SORTS, AND MY THOUGHTS (for what its worth)
The solution that I am going for (after emailing a app dev whose published a drive app that synchronizes without problems) is to use the Drive REST Api, rather than the newer (and Google preferred) Drive API. I tried limiting the 'requestSync' to only when the user navigated to a fragment with the Drive options (rather than every file transaction). However this would probably solve the requestSync rate limit for the most part, but it still could hit that limit. Also if multiple devices are running the app, and linked to the same Drive account were both syncing/uploading/deleting files at the same time in the app then there is a possibility of losing synchronization - maybe a rare scenario, but still possible. I don't feel that making a user wait to sync files is a viable option in terms of user experience or app design.
Curious though - The actual Drive app lets you refresh (requestSync?) as many times as you like. I created 20 folders on the web interface in quick succession, after each folder was created I refreshed the Drive app on my phone and it synced all 20 times. It seems Google understands the importance of synchronization, but chooses to make this quite difficult ensure this in their new Drive API. As already stated uploading files to the cloud storage happens usually instantly (it is queued, however if you have connectivity it happens almost straight away). I would have thought that this is the costly transaction in terms of moving data/updating drive contents, rather than just querying for synchronization of files. However you can keep adding files to your your drive app one at a time, and it uploads them one at a time almost instantly - where as you request sync more than 4 times in 30 seconds and it then fails and you have to 'cool off' for a while.
I'm very new to programming/Android in general - as soon as I learn something new, I realize how little I actually know, so if their is a better solution out there using the Drive API (rather than REST API) I'd very much welcome it.
DriveApi#requestSync will let you request a "sync down" from the server to the device. This way, on the second device you should be able to see the content uploaded from the first device. However, the calls to request sync are rate limited (per-device) to avoid abuse and guarantee a reasonable amount of data usage by the Drive API. You should ideally call request sync only when you need to. There's no way to increase the rate limit.
Regarding upload completion, after committing an upload, you shouldn't use request sync since that won't help you (it only syncs down but not up). The actual upload will happen as soon as possible (based on the device's connectivity and the preferences specified by your app through DrivePreferencesApi, which by default are unrestricted).
If you want to know when the actual upload happened, you can use CompletionEvents. This way you can test your app with more insights on what's actually going on.
So this is the first time I am going to send an update for my app and I don't know about what actully happens when an app is updated via google-play,
Here are some questions those I couldn't get answer of :
What is actually updated and how this process works i.e. methods or callbacks when update is done ?
What happens to the shared-preferences file, the name values pairs change/reset ?
Let's say I want to download some file from a server , when the app is updated via google play and do some db operations with that file in the background. How can I approach this in the right way.
--Edit--
To make it more clear I want to automatically do some processing when the app is updated by user and he doesn't bother to open the app and to achieve this I am looking for a trigger that is provided by google play to my app by any intent [implicit or explicit].
You need to implement a Broadcast Receiver that gets notified when the Paackage is beeing replaced:
In your Manifest include:
<receiver android:name="my.package.MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<data android:scheme="package" />
</intent-filter>
</receiver>
The class MyReceiver needs to extend android.content.BroadcastReceiver
To Answer your second question: The SharedPreferences aren't affected by an update through Google Play, as aren't the files in your App's data-Folder.
One way of checking if a new version has been installed is to use shared preferences. When the app is opened, you can check if an entry for that version is present. If it's not, a new version has been installed. After your processing is done, you can save the current version number in shared preferences.
As for your second question, shared preferences are not lost or reset by the update process. They stay as they were.
You may be out of luck here as there is no clean way to do this with an already installed app. If you have in-app billing (and this is speculation) and Google's in-app billing system has a dynamic dashboard or API to register users, then, you can say setup a new unique key of some sort and track it through this in-app billing like system. This may not be possible though.
The second thing you can do is to look up users in your server database and create this file you mention for all users and hold them in server cache (for fast access). Then in your app on first launch of this new version you can quickly get this file to user. This seems to be a good safe solution.
Good Luck, this is an interesting problem and I will be looking forward to hearing how you will solve this.
Just remembered, you can also look into push notifications and pushing this data to users. But, this assumes your app has this.
#prateek User need to start the App manually from Android 3.1 to apply the Broadcast Receiver Solution. I do not think you have any option...Sorry Mate...A small advice is that, try to put a push notification handler when you really want to do something with user interaction or a broadcast receiver to trigger frequent operations without User Interaction...Cheers!
You can approach in this way
in the first time user install and start the application, you should store the current version as last_vertion_of_app in the shared preferences. then use a alarm Manager +broadcast receiver+service to check the manifest app version with shared preference stored version if both are different(not equal) that means some update happen. then you can do the thing what you want to happen if updated=true in the save service even without application starts.