Skip to content

Commit

Permalink
Refactoring of Camera upload as Sync Provider
Browse files Browse the repository at this point in the history
This patch reworks most of the camera upload. It contains two important
changes:

(1) The old code implemented camera upload as a service. While that worked,
it proved somewhat problematic. Services can be killed by Android, and the
service had to be started by an Activity.

Android provides an API for implementing data syncing:
https://developer.android.com/training/sync-adapters/index.html

This API provides a number of advantages compared to a self-implemented
service. Among them are:

* Less code
* Automatic error recovery (e.g. retry on network errors)
* Periodic synchronisation (e.g. every 24h)
* Better visibility to the user (via Android Settings->Accounts)

(2) The old camera media sync algortihm tried to upload media via two ways:

a) when a new photo was added to the MediaStore, that photo was uploaded. But
*only* if the data plan allowed it.
b) when the user interacts with Seadroid, the service uploaded all photos
not yet uploaded. It did that by scanning certain hard-coded directories
on the externalStorage  and looking for new media files.

Important changes added by this patch:

- This new design builds soly on the MediaStore. The MediaStore contains
all images/videos on the phone. Therefore it should not be necessary to
scan (or even be aware of) where the photos are actually stored.

- The MediaStore has a column where it records the "DATA_ADDED" for every
media file. We can use that to sort and filter for new newly added media
files. This eases up the syncing mechanism considerably.

- Allowing to sync to sub directories in a repository.

- There is still a service necessary to monitor MediaStore activity. However
even without that service, Android will perform a sync about every 24h.
Also that service is now autostarted on System boot and on Seadroid
apk upgrades.

- Finally merging with existing files on the server was also improved by
skipping files that are already present on the server (it compares the file
size and will still upload if the sizes differ. That's probably the best
one can do with the current API2).

Overall, this patch should adresses the following issues:

issue haiwen#277 (Camera upload function dumps all subdir files into single dir)
issue haiwen#301 (Inconsistency in camera upload sync)
issue haiwen#311 (second suggestion: target repo sub directories)
issue haiwen#336 (file duplication when doing a full resync)
issue haiwen#375 (Picture upload not acting as service?)
issue haiwen#423 (Pictures never upload) [maybe. not sure what's going on there]

Useful links:
https://developer.android.com/training/sync-adapters/creating-sync-adapter.html
http://blog.udinic.com/2013/07/24/write-your-own-android-sync-adapter/
  • Loading branch information
forouher committed Jan 5, 2016
1 parent 4c346a1 commit 4a0b68d
Show file tree
Hide file tree
Showing 37 changed files with 1,464 additions and 2,008 deletions.
34 changes: 33 additions & 1 deletion AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- to start monitor services during boot -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<!-- to modify Seadroid's own account sync settings -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>

<!-- get list of Seadroid accounts -->
<uses-permission android:name="android.permission.GET_ACCOUNTS"
android:maxSdkVersion="22"/>
Expand Down Expand Up @@ -163,7 +170,14 @@
</provider>
<service android:name="com.seafile.seadroid2.transfer.TransferService" > </service>
<service android:name="com.seafile.seadroid2.monitor.FileMonitorService" > </service>
<service android:name="com.seafile.seadroid2.cameraupload.CameraUploadService" > </service>
<service android:name=".cameraupload.MediaObserverService" />

<provider
android:name=".cameraupload.StubContentProvider"
android:authorities="com.seafile.seadroid2.cameraupload"
android:label="@string/sync_provider_camera_upload"
android:exported="false"
android:syncable="true"/>

<service android:name=".account.AuthenticatorService" >
<intent-filter>
Expand All @@ -172,5 +186,23 @@
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
</service>

<service
android:name=".cameraupload.CameraSyncService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/cameraadapter" />
</service>

<receiver android:name="com.seafile.seadroid2.BootAutostart">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.ACTION_MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
</application>
</manifest>
5 changes: 5 additions & 0 deletions res/drawable/camera_bucket_selected.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#7EE6FF" />
<stroke android:width="5px" android:color="#00E6FF"/>
</shape>
43 changes: 43 additions & 0 deletions res/layout/bucket_item.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageView
android:id="@+id/bucket_item_thumbImage"
android:layout_width="512px"
android:layout_height="384px"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="centerCrop" />

<ImageView
android:id="@+id/bucket_item_text_background"
android:layout_width="512px"
android:layout_height="50px"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:alpha="0.5"
android:background="#000000" />

<ImageView
android:id="@+id/bucket_item_marking"
android:layout_width="512px"
android:layout_height="384px"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="@drawable/camera_bucket_selected"
android:alpha="0.7" />

<TextView
android:id="@+id/bucket_item_name"
android:layout_width="512px"
android:layout_height="50px"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:textColor="#ffffff"
android:layout_marginLeft="10px"
android:layout_marginTop="5px"
android:layout_marginRight="5px" />

</FrameLayout>
11 changes: 11 additions & 0 deletions res/layout/cuc_bucket_selection_layout.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<GridView android:id="@+id/cuc_bucket_selection_grid"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:numColumns="auto_fit" android:verticalSpacing="10dp"
android:horizontalSpacing="10dp" android:columnWidth="150dp"
android:stretchMode="columnWidth" android:gravity="center" />

</RelativeLayout>
2 changes: 1 addition & 1 deletion res/layout/cuc_local_directory_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/rb_margin_top"
android:text="@string/settings_cuc_pick_dir"
android:text="@string/settings_cuc_pick_albums"
android:textSize="@dimen/tv_subtitle_txt_size"/>
</RadioGroup>

Expand Down
5 changes: 3 additions & 2 deletions res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@
<string name="settings_cuc_remote_lib_account">Benutzerkonto auswählen</string>
<string name="settings_cuc_remote_lib_repo">%s wurde ausgewählt</string>
<string name="settings_cuc_upload_to_parent_folder">Zum übergeordneten Ordner</string>
<string name="settings_cuc_auto_scan">Alle Bilder auf dem Gerät</string>
<string name="settings_cuc_pick_dir">Bilderordner auswählen</string>
<string name="settings_cuc_auto_scan">Alle Kamerabilder auf dem Gerät</string>
<string name="settings_cuc_pick_albums">Alben auswählen</string>
<string name="settings_cuc_remote_lib_hint">Wählen Sie aus, welches Benutzerkonto und welche Bibliothek Sie bevorzugen.\nSie können beides in den Einstellungen jederzeit anpassen.</string>
<string name="settings_cuc_finish_title">Beenden</string>
<string name="settings_cuc_ready">Seafile wird nun die Dateien aus den ausgewählten Ordnern hochladen. \nSie können die App währenddessen schließen.</string>
Expand Down Expand Up @@ -422,4 +422,5 @@
<string name="overwrite_existing_file_exist">Die Datei ist schon vorhanden</string>
<!--Action Mode-->
<string name="action_mode_no_items_selected">Es wurden keine Elemente ausgewählt</string>
<string name="sync_provider_camera_upload">Kamerafotos und -videos</string>
</resources>
20 changes: 12 additions & 8 deletions res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
<string name="no_account">Please set up an account in Seafile first</string>
<string name="choose_an_account">Choose an account</string>
<string name="choose_a_library">Choose a library</string>
<string name="choose_a_folder">Choose a folder</string>
<string name="select_galleries">Select galleries</string>
<string name="load_accounts_fail">Failed to load Seafile accounts</string>
<string name="load_libraries_fail">Failed to load libraries</string>
<string name="load_dir_fail">Failed to load contents of this folder</string>
Expand Down Expand Up @@ -282,14 +282,14 @@
<string name="settings_camera_upload_change_repo_title">Change Upload Library</string>
<string name="settings_camera_upload_repo_hint">Please choose a library first</string>
<string name="settings_camera_upload_turn_on">Turn on Camera Upload</string>
<string name="settings_camera_upload_choose_dir">Change Local Folders</string>
<string name="settings_camera_upload_choose_dir">Change albums</string>
<string name="settings_camera_upload_dir_auto_scan">Auto scan photos by default</string>
<string name="settings_camera_upload_dir_custom">Scan selected folders</string>
<string name="settings_camera_upload_dir_custom">Scan selected albums</string>
<string name="settings_camera_upload_lib_not_found">The selected library does not exist</string>
<string name="settings_camera_upload_advanced_feature_title">Advanced Options</string>
<string name="settings_camera_upload_advanced_feature_subtitle">Custom upload options</string>
<string name="settings_camera_upload_advanced_custom_directories">Custom Upload Folders</string>
<string name="settings_camera_upload_advanced_custom_directories_on">Pick photo folders</string>
<string name="settings_camera_upload_advanced_custom_directories">Custom Upload Albums</string>
<string name="settings_camera_upload_advanced_custom_directories_on">Pick photo albums</string>
<string name="settings_camera_upload_advanced_custom_directories_off">Auto scan device</string>

<!-- Camera Upload Configuration View -->
Expand All @@ -302,7 +302,7 @@
<string name="settings_cuc_type_title">What to upload</string>
<string name="settings_cuc_type_photo">Photos only</string>
<string name="settings_cuc_type_video">Photos and videos</string>
<string name="settings_cuc_local_dir_title">Select Local Folders</string>
<string name="settings_cuc_local_dir_title">Select albums</string>
<plurals name="settings_cuc_local_dir_items">
<item quantity="one">%1$d item</item>
<item quantity="other">%1$d items</item>
Expand All @@ -311,8 +311,8 @@
<string name="settings_cuc_remote_lib_account">Choose an account</string>
<string name="settings_cuc_remote_lib_repo">%s was selected</string>
<string name="settings_cuc_upload_to_parent_folder">Up to Parent Folder</string>
<string name="settings_cuc_auto_scan">Get all photos on device</string>
<string name="settings_cuc_pick_dir">Let me pick my photo folders</string>
<string name="settings_cuc_auto_scan">Get all camera photos on device</string>
<string name="settings_cuc_pick_albums">Let me pick my photo albums</string>
<string name="settings_cuc_remote_lib_hint">Choose your preferred account and library. \nYou can change these options later in Settings.</string>
<string name="settings_cuc_finish_title">Finish</string>
<string name="settings_cuc_ready">Seafile will now uploading files from your selected folders. \nFeel free to close the app during this process.</string>
Expand Down Expand Up @@ -451,4 +451,8 @@

<!--Action Mode -->
<string name="action_mode_no_items_selected">No items were selected</string>
<string name="sync_provider_camera_upload">Camera photos and videos</string>
<string name="camera_sync_notification_title_failed">Camera upload failed</string>
<string name="camera_sync_notification_repo_missing_failed">Server repository is missing.</string>
<string name="camera_sync_notification_auth_error_failed">Authentication error.</string>
</resources>
9 changes: 9 additions & 0 deletions res/xml/cameraadapter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.seafile.seadroid2.cameraupload"
android:accountType="com.seafile.seadroid2"
android:userVisible="true"
android:supportsUploading="true"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"/>
4 changes: 2 additions & 2 deletions res/xml/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@
</CheckBoxPreference>
<CheckBoxPreference
android:defaultValue="false"
android:key="camera_upload_directory_switch_key"
android:key="camera_upload_buckets_switch_key"
android:summaryOff="@string/settings_camera_upload_advanced_custom_directories_off"
android:summaryOn="@string/settings_camera_upload_advanced_custom_directories_on"
android:title="@string/settings_camera_upload_advanced_custom_directories"
android:layout="@layout/seafile_checkbox_layout" >
</CheckBoxPreference>
<Preference
android:key="camera_upload_directory_key"
android:key="camera_upload_buckets_key"
android:layout="@layout/preference"
android:summary="@string/settings_camera_upload_dir_auto_scan"
android:title="@string/settings_camera_upload_choose_dir" >
Expand Down
32 changes: 32 additions & 0 deletions src/com/seafile/seadroid2/BootAutostart.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.seafile.seadroid2;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.seafile.seadroid2.cameraupload.MediaObserverService;

/**
* This receiver is called whenever the system has booted or
* the Seadroid app has been upgraded to a new version.
*
* It can be used to start up background services.
*/
public class BootAutostart extends BroadcastReceiver {
private static final String DEBUG_TAG = "BootAutostart";

/**
* This method will be excecuted after
* - booting the device
* - upgrade of the Seadroid package
*/
public void onReceive(Context context, Intent intent)
{
Log.i(DEBUG_TAG, "Registering for MediaProvider changes.");

Intent mediaObserver = new Intent(context, MediaObserverService.class);
context.startService(mediaObserver);
}

}

0 comments on commit 4a0b68d

Please sign in to comment.