Browse Source

Enabled again 'basic' recovery of instant uploads on WiFi connection

David A. Velasco 9 years ago
parent
commit
52d05b37d4

+ 1 - 1
AndroidManifest.xml

@@ -175,7 +175,7 @@
         <receiver android:name=".files.services.ConnectivityActionReceiver"
 		    android:enabled="true" android:label="ConnectivityActionReceiver">
 		    <intent-filter>
-		        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
+		        <!--action android:name="android.net.conn.CONNECTIVITY_CHANGE"/-->
 		        <action android:name="android.net.wifi.STATE_CHANGE"/>
 		    </intent-filter>
 		</receiver>

+ 1 - 0
src/com/owncloud/android/datamodel/UploadsStorageManager.java

@@ -109,6 +109,7 @@ public class UploadsStorageManager extends Observable {
         cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value);
         cv.put(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR, ocUpload.getLocalAction());
         cv.put(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE, ocUpload.isForceOverwrite() ? 1 : 0);
+        cv.put(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER, ocUpload.isCreateRemoteFolder() ? 1 : 0);
         cv.put(ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY, ocUpload.isWhileChargingOnly() ? 1 : 0);
         cv.put(ProviderTableMeta.UPLOADS_IS_WIFI_ONLY, ocUpload.isUseWifiOnly() ? 1 : 0);
         cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue());

+ 1 - 0
src/com/owncloud/android/db/UploadResult.java

@@ -77,6 +77,7 @@ public enum UploadResult {
             case HOST_NOT_AVAILABLE:
             case TIMEOUT:
             case WRONG_CONNECTION:
+            case DELAYED_FOR_WIFI:
                 return NETWORK_CONNECTION;
             case ACCOUNT_EXCEPTION:
             case UNAUTHORIZED:

+ 109 - 47
src/com/owncloud/android/files/services/ConnectivityActionReceiver.java

@@ -25,11 +25,13 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 
+import com.owncloud.android.db.PreferenceReader;
+import com.owncloud.android.db.UploadResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
 /**
@@ -40,11 +42,20 @@ import com.owncloud.android.lib.common.utils.Log_OC;
  * Later can be added: - Signal connectivity to download service, deletion
  * service, ... - Handle offline mode (cf.
  * https://github.com/owncloud/android/issues/162)
- * 
+ *
+ * Have fun with the comments :S
  */
 public class ConnectivityActionReceiver extends BroadcastReceiver {
     private static final String TAG = ConnectivityActionReceiver.class.getSimpleName();
 
+    /**
+     * Magic keyword, by Google.
+     *
+     * {@See http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()}
+     */
+    private static final String UNKNOWN_SSID = "<unknown ssid>";
+
+
     @Override
     public void onReceive(final Context context, Intent intent) {
         // LOG ALL EVENTS:
@@ -59,65 +70,116 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
             Log_OC.v(TAG, "no extras");
         }
 
-        
         /**
-         * Just checking for State.CONNECTED will be not good enough, as it ends here multiple times.
-         * Work around from:
-         * http://stackoverflow.com/
-         * questions/17287178/connectivitymanager-getactivenetworkinfo-returning-true-when-internet-is-off
-         */            
+         * There is an interesting mess to process WifiManager.NETWORK_STATE_CHANGED_ACTION and
+         * ConnectivityManager.CONNECTIVITY_ACTION in a simple and reliable way.
+         *
+         * The former triggers much more events than what we really need to know about Wifi connection.
+         *
+         * But there are annoying uncertainties about ConnectivityManager.CONNECTIVITY_ACTION due
+         * to the deprecation of ConnectivityManager.EXTRA_NETWORK_INFO in API level 14, and the absence
+         * of ConnectivityManager.EXTRA_NETWORK_TYPE until API level 17. Dear Google, how should we
+         * handle API levels 14 to 16?
+         *
+         * In the end maybe we need to keep in memory the current knowledge about connectivity
+         * and update it taking into account several Intents received in a row
+         *
+         * But first let's try something "simple" to keep a basic retry of instant uploads in
+         * version 1.9.2, similar to the existent until 1.9.1. To be improved.
+         */
         if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
             NetworkInfo networkInfo =
                 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-            if(networkInfo.isConnected()) {
-                Log_OC.d(TAG, "Wifi is connected: " + String.valueOf(networkInfo));
+            WifiInfo wifiInfo =
+                intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
+            String bssid =
+                intent.getStringExtra(WifiManager.EXTRA_BSSID);
+            if(networkInfo.isConnected()   &&      // not enough; see (*) right below
+                wifiInfo != null    &&
+                !UNKNOWN_SSID.equals(wifiInfo.getSSID().toLowerCase()) &&
+                bssid != null
+                    ) {
+                Log_OC.d(TAG, "WiFi connected");
+
                 wifiConnected(context);
+            } else {
+                // TODO tons of things to check to conclude disconnection;
+                // TODO maybe alternative commented below, based on CONNECTIVITY_ACTION is better
+                Log_OC.d(TAG, "WiFi disconnected ... but don't know if right now");
             }
-        } else if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-            ConnectivityManager cm =
-                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-            NetworkInfo networkInfo = cm.getActiveNetworkInfo();
-            if(networkInfo == null || networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
-                ! networkInfo.isConnected()) {
-                Log_OC.d(TAG, "Wifi is disconnected: " + String.valueOf(networkInfo));
-                wifiDisconnected(context);
-            }
+         }
+        // (*) When WiFi is lost, an Intent with network state CONNECTED and SSID "<unknown ssid>" is
+        //      received right before another Intent with network state DISCONNECTED; needs to
+        //      be differentiated of a new Wifi connection.
+        //
+        //  Besides, with a new connection two Intents are received, having only the second the extra
+        //  WifiManager.EXTRA_BSSID, with the BSSID of the access point accessed.
+        //
+        //  Not sure if this protocol is exact, since it's not documented. Only found mild references in
+        //   - http://developer.android.com/intl/es/reference/android/net/wifi/WifiInfo.html#getSSID()
+        //   - http://developer.android.com/intl/es/reference/android/net/wifi/WifiManager.html#EXTRA_BSSID
+        //  and reproduced in Nexus 5 with Android 6.
+
+
+        /**
+         * Possible alternative attending ConnectivityManager.CONNECTIVITY_ACTION.
+         *
+         * Let's see what QA has to say
+         *
+        if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+            NetworkInfo networkInfo = intent.getParcelableExtra(
+                    ConnectivityManager.EXTRA_NETWORK_INFO      // deprecated in API 14
+            );
+            int networkType = intent.getIntExtra(
+                    ConnectivityManager.EXTRA_NETWORK_TYPE,     // only from API level 17
+                    -1
+            );
+            boolean couldBeWifiAction =
+                    (networkInfo == null && networkType < 0)    ||      // cases of lack of info
+                    networkInfo.getType() == ConnectivityManager.TYPE_WIFI  ||
+                    networkType == ConnectivityManager.TYPE_WIFI;
+
+            if (couldBeWifiAction) {
+                if (ConnectivityUtils.isAppConnectedViaWiFi(context)) {
+                    Log_OC.d(TAG, "WiFi connected");
+                    wifiConnected(context);
+                } else {
+                    Log_OC.d(TAG, "WiFi disconnected");
+                    wifiDisconnected(context);
+                }
+            } /* else, CONNECTIVIY_ACTION is (probably) about other network interface (mobile, bluetooth, ...)
         }
-        
+        */
     }
 
     private void wifiConnected(Context context) {
-        //Log_OC.d(TAG, "FileUploader.retry() called by onReceive()");
-        //FileUploader.retry(context);
-        Log_OC.w(TAG, "Automatic retry of uploads on WiFi recovery is temporarily disabled due to dev in progress");
+        // for the moment, only recovery of instant uploads, similar to behaviour in release 1.9.1
+        // (with some side effects that improve it a bit, but needs to be better)
+        if (
+                (PreferenceReader.instantPictureUploadEnabled(context) &&
+                        PreferenceReader.instantPictureUploadViaWiFiOnly(context)) ||
+                (PreferenceReader.instantVideoUploadEnabled(context) &&
+                        PreferenceReader.instantPictureUploadViaWiFiOnly(context))
+                ) {
+            Log_OC.d(TAG, "Requesting retry of instant uploads (& friends)");
+            FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+            requester.retryUploads(
+                    context,
+                    null,
+                    UploadResult.NETWORK_CONNECTION
+            );
+        }
     }
 
     private void wifiDisconnected(Context context) {
-        // This is not needed anymore after refactoring FileUploader service;
-        //  - if any upload is in progress, it will be interrupted due to the lack of connectivity while
-        //      the device reconnects through
-        //  - if other instant uploads are queued and the current settings requires 'Wifi only', FileUploader
-        //      will not execute them, since this is now checked when the upload is starting, not when it's
-        //      requested, see FileUploader#uploadFile(...)
-        //
-        // Leaving commented for a (short) while
-        /*
-        boolean instantPictureWiFiOnly = PreferenceReader.instantPictureUploadViaWiFiOnly(context);
-        boolean instantVideoWiFiOnly = PreferenceReader.instantVideoUploadViaWiFiOnly(context);
-        if (instantPictureWiFiOnly || instantVideoWiFiOnly) {
-            Account account = AccountUtils.getCurrentOwnCloudAccount(context);
-            if (account == null) {
-                Log_OC.w(TAG, "No account found for instant upload, aborting");
-                return;
-            }
+        // TODO something smart
 
-            Intent i = new Intent(context, FileUploader.class);
-            i.putExtra(FileUploader.KEY_ACCOUNT, account);
-            i.putExtra(FileUploader.KEY_CANCEL_ALL, true);
-            // TODO improve with extra options to cancel selected uploads: instant_pictures, instant_videos, ...
-            context.startService(i);
-        }
-        */
+        // NOTE: explicit cancellation of only-wifi instant uploads is not needed anymore, since currently:
+        //  - any upload in progress will be interrupted due to the lack of connectivity while the device
+        //      reconnects through other network interface;
+        //  - FileUploader checks instant upload settings and connection state before executing each
+        //    upload operation, so other pending instant uploads after the current one will not be run
+        //    (currently are silently moved to FAILED state)
     }
 
 

+ 25 - 13
src/com/owncloud/android/files/services/FileUploader.java

@@ -259,28 +259,40 @@ public class FileUploader extends Service
             uploadUpdate(context, account, new OCFile[]{existingFile}, behaviour, forceOverwrite);
         }
 
-        public void retryUploadsForAccount(Account account, Context context) {
-            UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(context.getContentResolver());
-            OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
-            for ( OCUpload upload: failedUploads){
-                if (upload.getAccountName().equals(account.name) &&
-                        upload.getLastResult() == UploadResult.CREDENTIAL_ERROR ) {
-                    retry(context, account, upload);
-                }
-            }
-        }
 
         /**
          * Call to retry upload identified by remotePath
          */
         public void retry(Context context, Account account, OCUpload upload) {
-            Intent i = new Intent(context, FileUploader.class);
-            i.putExtra(FileUploader.KEY_RETRY, true);
             if (upload != null) {
+                Intent i = new Intent(context, FileUploader.class);
+                i.putExtra(FileUploader.KEY_RETRY, true);
                 i.putExtra(FileUploader.KEY_ACCOUNT, account);
                 i.putExtra(FileUploader.KEY_RETRY_UPLOAD, upload);
+                context.startService(i);
+            }
+        }
+
+        /**
+         * TODO - improve interface, but now just recovering basic auto-retry of instant uploads on Wifi connection
+         *
+         * @param context
+         * @param account           Null to retry uploads in current account
+         * @param uploadResult
+         */
+        public void retryUploads(Context context, Account account, UploadResult uploadResult) {
+            UploadsStorageManager uploadsStorageManager = new UploadsStorageManager(context.getContentResolver());
+            OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads();
+            if (account == null) {
+                account = AccountUtils.getCurrentOwnCloudAccount(context);
+            }
+            for ( OCUpload upload: failedUploads){
+                if (upload.getAccountName().equals(account.name) &&
+                        upload.getLastResult() == uploadResult ) {
+
+                    retry(context, account, upload);
+                }
             }
-            context.startService(i);
         }
 
     }

+ 2 - 1
src/com/owncloud/android/ui/activity/UploadListActivity.java

@@ -42,6 +42,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.UploadsStorageManager;
 import com.owncloud.android.db.OCUpload;
+import com.owncloud.android.db.UploadResult;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -224,7 +225,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         if (requestCode == UPDATE_CREDENTIALS_REQUEST_CODE && resultCode == FileActivity.RESULT_OK) {
             // Retry uploads of this account
             FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
-            requester.retryUploadsForAccount(getAccount(), this);
+            requester.retryUploads(this, getAccount(), UploadResult.CREDENTIAL_ERROR);
         }
     }
 

+ 9 - 1
src/com/owncloud/android/utils/ConnectivityUtils.java

@@ -20,16 +20,24 @@
 package com.owncloud.android.utils;
 
 import android.content.Context;
+import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 
+import com.owncloud.android.lib.common.utils.Log_OC;
+
 public class ConnectivityUtils {
 
+    private final static String TAG = ConnectivityUtils.class.getName();
+
     public static boolean isAppConnectedViaWiFi(Context context) {
         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        return cm != null && cm.getActiveNetworkInfo() != null
+        boolean result =
+                cm != null && cm.getActiveNetworkInfo() != null
                 && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
                 && cm.getActiveNetworkInfo().getState() == NetworkInfo.State.CONNECTED;
+        Log_OC.d(TAG, "is AppConnectedViaWifi returns " + result);
+        return result;
     }
 
     public static boolean isAppConnected(Context context) {