浏览代码

Merge pull request #149 from owncloud/videoInstandUploads

Issue #136  [Feature] Instant upload of videos
Javier Gonzalez 11 年之前
父节点
当前提交
d60ddd81a0

+ 4 - 0
AndroidManifest.xml

@@ -174,6 +174,10 @@
                 <action android:name="android.hardware.action.NEW_PICTURE" />
                 <action android:name="android.hardware.action.NEW_PICTURE" />
                 <data android:mimeType="image/*" />
                 <data android:mimeType="image/*" />
             </intent-filter>
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.hardware.action.NEW_VIDEO" />
+                <data android:mimeType="video/*" />
+            </intent-filter>
             <intent-filter>
             <intent-filter>
                 <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
                 <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
             </intent-filter>
             </intent-filter>

+ 5 - 2
res/values/strings.xml

@@ -18,8 +18,10 @@
     <string name="prefs_manage_accounts">Manage Accounts</string>
     <string name="prefs_manage_accounts">Manage Accounts</string>
     <string name="prefs_pincode">App PIN</string>
     <string name="prefs_pincode">App PIN</string>
     <string name="prefs_pincode_summary">Protect your client</string>
     <string name="prefs_pincode_summary">Protect your client</string>
-    <string name="prefs_instant_upload">Enable instant uploads</string>
-    <string name="prefs_instant_upload_summary">Instantly upload photos taken by camera</string>
+    <string name="prefs_instant_upload">Instant picture uploads</string>
+    <string name="prefs_instant_upload_summary">Instantly upload pictures taken by camera</string>
+    <string name="prefs_instant_video_upload">Instant video uploads</string>
+    <string name="prefs_instant_video_upload_summary">Instantly upload videos recorded by camera</string>
     <string name="prefs_log_title">Enable Logging</string>
     <string name="prefs_log_title">Enable Logging</string>
     <string name="prefs_log_summary">This is used to log problems</string>
     <string name="prefs_log_summary">This is used to log problems</string>
     <string name="prefs_log_title_history">Logging History</string>
     <string name="prefs_log_title_history">Logging History</string>
@@ -232,6 +234,7 @@
     <string name="placeholder_media_time">12:23:45</string>
     <string name="placeholder_media_time">12:23:45</string>
     
     
     <string name="instant_upload_on_wifi">Upload pictures via WiFi only</string>
     <string name="instant_upload_on_wifi">Upload pictures via WiFi only</string>
+    <string name="instant_video_upload_on_wifi">Upload videos via WiFi only</string>
     <string name="instant_upload_path">/InstantUpload</string>
     <string name="instant_upload_path">/InstantUpload</string>
     <string name="conflict_title">Update conflict</string>
     <string name="conflict_title">Update conflict</string>
     <string name="conflict_message">Remote file %s is not synchronized with local file. Continuing will replace content of file on server.</string>
     <string name="conflict_message">Remote file %s is not synchronized with local file. Continuing will replace content of file on server.</string>

+ 7 - 0
res/xml/preferences.xml

@@ -35,6 +35,13 @@
         				android:disableDependentsState="true" 
         				android:disableDependentsState="true" 
         				android:title="@string/instant_upload_on_wifi" 
         				android:title="@string/instant_upload_on_wifi" 
         				android:key="instant_upload_on_wifi"/>
         				android:key="instant_upload_on_wifi"/>
+    <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_video_uploading" 
+                        android:title="@string/prefs_instant_video_upload"  
+                        android:summary="@string/prefs_instant_video_upload_summary"/>
+    <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:dependency="instant_video_uploading" 
+        				android:disableDependentsState="true" 
+        				android:title="@string/instant_video_upload_on_wifi" 
+        				android:key="instant_video_upload_on_wifi"/>
     <!-- DISABLED FOR RELEASE UNTIL FIXED 
     <!-- DISABLED FOR RELEASE UNTIL FIXED 
     CheckBoxPreference android:key="log_to_file" 
     CheckBoxPreference android:key="log_to_file" 
                         android:title="@string/prefs_log_title"  
                         android:title="@string/prefs_log_title"  

+ 13 - 8
src/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -153,6 +153,7 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
     
     
     private boolean mServerIsChecked = false;
     private boolean mServerIsChecked = false;
     private boolean mServerIsValid = false;
     private boolean mServerIsValid = false;
+    private boolean mPendingAutoCheck = false;
 
 
     private GetServerInfoOperation.ServerInfo mServerInfo = 
     private GetServerInfoOperation.ServerInfo mServerInfo = 
             new GetServerInfoOperation.ServerInfo();
             new GetServerInfoOperation.ServerInfo();
@@ -174,7 +175,7 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
     
     
     /// Identifier of operation in progress which result shouldn't be lost 
     /// Identifier of operation in progress which result shouldn't be lost 
     private long mWaitingForOpId = Long.MAX_VALUE;
     private long mWaitingForOpId = Long.MAX_VALUE;
-    
+
     
     
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
@@ -219,6 +220,8 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
         /// initialize general UI elements
         /// initialize general UI elements
         initOverallUi(savedInstanceState);
         initOverallUi(savedInstanceState);
         
         
+        mOkButton = findViewById(R.id.buttonOK);
+
         /// initialize block to be moved to single Fragment to check server and get info about it 
         /// initialize block to be moved to single Fragment to check server and get info about it 
         initServerPreFragment(savedInstanceState);
         initServerPreFragment(savedInstanceState);
         
         
@@ -404,12 +407,9 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
         });
         });
      
      
         
         
-        /// step 4 - automatic actions to start
-        if (savedInstanceState == null) {
-            if (mAction != ACTION_CREATE || !isUrlInputAllowed) {
-                checkOcServer(); 
-            }
-        }
+        /// step 4 - mark automatic check to be started when OperationsService is ready
+        mPendingAutoCheck = (savedInstanceState == null && 
+                (mAction != ACTION_CREATE || !isUrlInputAllowed));
     }
     }
     
     
     
     
@@ -426,7 +426,6 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
         mUsernameInput = (EditText) findViewById(R.id.account_username);
         mUsernameInput = (EditText) findViewById(R.id.account_username);
         mPasswordInput = (EditText) findViewById(R.id.account_password);
         mPasswordInput = (EditText) findViewById(R.id.account_password);
         mAuthStatusView = (TextView) findViewById(R.id.auth_status_text); 
         mAuthStatusView = (TextView) findViewById(R.id.auth_status_text); 
-        mOkButton = findViewById(R.id.buttonOK);
         
         
         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState)
         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState)
         String presetUserName = null;
         String presetUserName = null;
@@ -728,6 +727,8 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
             if (mOperationsServiceBinder != null) {
             if (mOperationsServiceBinder != null) {
                 //Log_OC.wtf(TAG, "checking server..." );
                 //Log_OC.wtf(TAG, "checking server..." );
                 mWaitingForOpId = mOperationsServiceBinder.newOperation(getServerInfoIntent);
                 mWaitingForOpId = mOperationsServiceBinder.newOperation(getServerInfoIntent);
+            } else {
+              Log_OC.wtf(TAG, "Server check tried with OperationService unbound!" );
             }
             }
             
             
         } else {
         } else {
@@ -1744,6 +1745,10 @@ SsoWebViewClientListener, OnSslUntrustedCertListener {
         if (mWaitingForOpId <= Integer.MAX_VALUE) {
         if (mWaitingForOpId <= Integer.MAX_VALUE) {
             mOperationsServiceBinder.dispatchResultIfFinished((int)mWaitingForOpId, this);
             mOperationsServiceBinder.dispatchResultIfFinished((int)mWaitingForOpId, this);
         }
         }
+        
+        if (mPendingAutoCheck) {
+            checkOcServer();
+        }
     }
     }
 
 
     
     

+ 90 - 54
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java

@@ -1,6 +1,6 @@
 /* ownCloud Android client application
 /* ownCloud Android client application
  *   Copyright (C) 2012  Bartek Przybylski
  *   Copyright (C) 2012  Bartek Przybylski
- *   Copyright (C) 2012-2013 ownCloud Inc.
+ *   Copyright (C) 2012-2014 ownCloud Inc.
  *
  *
  *   This program is free software: you can redistribute it and/or modify
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *   it under the terms of the GNU General Public License version 2,
@@ -32,23 +32,26 @@ import android.accounts.Account;
 import android.content.BroadcastReceiver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Intent;
-//import android.content.IntentFilter;
 import android.database.Cursor;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo.State;
 import android.net.NetworkInfo.State;
 import android.preference.PreferenceManager;
 import android.preference.PreferenceManager;
-import android.provider.MediaStore.Images.Media;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
 import android.webkit.MimeTypeMap;
 import android.webkit.MimeTypeMap;
 
 
 
 
 public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
 
-    private static String TAG = "InstantUploadBroadcastReceiver";
-    private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
-    //Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
+    private static String TAG = InstantUploadBroadcastReceiver.class.getName();
+    // Image action
+    // Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
     private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
     private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
-    //Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
+    // Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
     private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
     private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
+    // Video action
+    // Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_VIDEO
+    private static String NEW_VIDEO_ACTION = "android.hardware.action.NEW_VIDEO";
 
 
     @Override
     @Override
     public void onReceive(Context context, Intent intent) {
     public void onReceive(Context context, Intent intent) {
@@ -56,35 +59,29 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
         if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
             handleConnectivityAction(context, intent);
             handleConnectivityAction(context, intent);
         }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
         }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
-            handleNewPhotoAction(context, intent);
+            handleNewPictureAction(context, intent); 
             Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
             Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
         } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
         } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
-            handleNewPhotoAction(context, intent);
+            handleNewPictureAction(context, intent); 
             Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
             Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
-        } else if (intent.getAction().equals(FileUploader.getUploadFinishMessage())) {
-            handleUploadFinished(context, intent);
+        } else if (intent.getAction().equals(NEW_VIDEO_ACTION)) {
+            Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_VIDEO");
+            handleNewVideoAction(context, intent);
         } else {
         } else {
             Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
             Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
         }
         }
     }
     }
 
 
-    private void handleUploadFinished(Context context, Intent intent) {
-        // remove successfull uploading, ignore rest for reupload on reconnect
-        /*
-        if (intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false)) {
-            DbHandler db = new DbHandler(context);
-            String localPath = intent.getStringExtra(FileUploader.EXTRA_OLD_FILE_PATH);
-            if (!db.removeIUPendingFile(localPath)) {
-                Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
-            }
-            db.close();
-        }
-        */
-    }
+    private void handleNewPictureAction(Context context, Intent intent) {
+        Cursor c = null;
+        String file_path = null;
+        String file_name = null;
+        String mime_type = null;
 
 
-    private void handleNewPhotoAction(Context context, Intent intent) {
-        if (!instantUploadEnabled(context)) {
-            Log_OC.d(TAG, "Instant upload disabled, aborting uploading");
+        Log_OC.w(TAG, "New photo received");
+        
+        if (!instantPictureUploadEnabled(context)) {
+            Log_OC.d(TAG, "Instant picture upload disabled, ignoring new picture");
             return;
             return;
         }
         }
 
 
@@ -94,39 +91,72 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
             return;
             return;
         }
         }
 
 
-        Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
-
+        String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE };
+        c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
         if (!c.moveToFirst()) {
         if (!c.moveToFirst()) {
             Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
             Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
             return;
             return;
         }
         }
-
-        String file_path = c.getString(c.getColumnIndex(Media.DATA));
-        String file_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME));
-        String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
-
+        file_path = c.getString(c.getColumnIndex(Images.Media.DATA));
+        file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME));
+        mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE));
         c.close();
         c.close();
-        Log_OC.e(TAG, file_path + "");
+        
+        Log_OC.d(TAG, file_path + "");
 
 
-        // same always temporally the picture to upload
+        // save always temporally the picture to upload
         DbHandler db = new DbHandler(context);
         DbHandler db = new DbHandler(context);
         db.putFileForLater(file_path, account.name, null);
         db.putFileForLater(file_path, account.name, null);
         db.close();
         db.close();
 
 
-        if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
+        if (!isOnline(context) || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
+            return;
+        }
+
+        Intent i = new Intent(context, FileUploader.class);
+        i.putExtra(FileUploader.KEY_ACCOUNT, account);
+        i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
+        i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, file_name));
+        i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+        i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
+        i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+        context.startService(i);
+    }
+
+    private void handleNewVideoAction(Context context, Intent intent) {
+        Cursor c = null;
+        String file_path = null;
+        String file_name = null;
+        String mime_type = null;
+
+        Log_OC.w(TAG, "New video received");
+        
+        if (!instantVideoUploadEnabled(context)) {
+            Log_OC.d(TAG, "Instant video upload disabled, ignoring new video");
             return;
             return;
         }
         }
 
 
-        // register for upload finishe message
-        // there is a litte problem with android API, we can register for
-        // particular
-        // intent in registerReceiver but we cannot unregister from precise
-        // intent
-        // we can unregister from entire listenings but thats suck a bit.
-        // On the other hand this might be only for dynamicly registered
-        // broadcast receivers, needs investigation.
-        /*IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
-        context.getApplicationContext().registerReceiver(this, filter);*/
+        Account account = AccountUtils.getCurrentOwnCloudAccount(context);
+        if (account == null) {
+            Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
+            return;
+        }
+
+        String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE };
+        c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
+        if (!c.moveToFirst()) {
+            Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
+            return;
+        } 
+        file_path = c.getString(c.getColumnIndex(Video.Media.DATA));
+        file_name = c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME));
+        mime_type = c.getString(c.getColumnIndex(Video.Media.MIME_TYPE));
+        c.close();
+        Log_OC.d(TAG, file_path + "");
+
+        if (!isOnline(context) || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
+            return;
+        }
 
 
         Intent i = new Intent(context, FileUploader.class);
         Intent i = new Intent(context, FileUploader.class);
         i.putExtra(FileUploader.KEY_ACCOUNT, account);
         i.putExtra(FileUploader.KEY_ACCOUNT, account);
@@ -140,19 +170,17 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
     }
     }
 
 
     private void handleConnectivityAction(Context context, Intent intent) {
     private void handleConnectivityAction(Context context, Intent intent) {
-        if (!instantUploadEnabled(context)) {
-            Log_OC.d(TAG, "Instant upload disabled, abording uploading");
+        if (!instantPictureUploadEnabled(context)) {
+            Log_OC.d(TAG, "Instant upload disabled, don't upload anything");
             return;
             return;
         }
         }
 
 
         if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
         if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
                 && isOnline(context)
                 && isOnline(context)
-                && (!instantUploadViaWiFiOnly(context) || (instantUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
+                && (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
             DbHandler db = new DbHandler(context);
             DbHandler db = new DbHandler(context);
             Cursor c = db.getAwaitingFiles();
             Cursor c = db.getAwaitingFiles();
             if (c.moveToFirst()) {
             if (c.moveToFirst()) {
-                //IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
-                //context.getApplicationContext().registerReceiver(this, filter);
                 do {
                 do {
                     String account_name = c.getString(c.getColumnIndex("account"));
                     String account_name = c.getString(c.getColumnIndex("account"));
                     String file_path = c.getString(c.getColumnIndex("path"));
                     String file_path = c.getString(c.getColumnIndex("path"));
@@ -202,11 +230,19 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
                 && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
                 && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
     }
     }
 
 
-    public static boolean instantUploadEnabled(Context context) {
+    public static boolean instantPictureUploadEnabled(Context context) {
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
     }
     }
 
 
-    public static boolean instantUploadViaWiFiOnly(Context context) {
+    public static boolean instantVideoUploadEnabled(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_uploading", false);
+    }
+
+    public static boolean instantPictureUploadViaWiFiOnly(Context context) {
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
         return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
     }
     }
+    
+    public static boolean instantVideoUploadViaWiFiOnly(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_wifi", false);
+    }
 }
 }

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

@@ -465,7 +465,7 @@ public class InstantUploadActivity extends Activity {
     private boolean canInstantUpload() {
     private boolean canInstantUpload() {
 
 
         if (!InstantUploadBroadcastReceiver.isOnline(this)
         if (!InstantUploadBroadcastReceiver.isOnline(this)
-                || (InstantUploadBroadcastReceiver.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
+                || (InstantUploadBroadcastReceiver.instantPictureUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
                         .isConnectedViaWiFi(this))) {
                         .isConnectedViaWiFi(this))) {
             return false;
             return false;
         } else {
         } else {

+ 25 - 24
src/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -196,31 +196,32 @@ public class OCFileListFragment extends ExtendedListFragment implements EditName
      * Restore index and position
      * Restore index and position
      */
      */
     private void restoreIndexAndTopPosition() {
     private void restoreIndexAndTopPosition() {
-        int index = mIndexes.get(mIndexes.size() - 1);
-        mIndexes.remove(mIndexes.size() - 1);
-        
-        int firstPosition = mFirstPositions.get(mFirstPositions.size() - 1);
-        mFirstPositions.remove(mFirstPositions.size() -1);
-        
-        int top = mTops.get(mTops.size() - 1);
-        mTops.remove(mTops.size() - 1);
-        
-        mList.setSelectionFromTop(firstPosition, top);
-        
-        // Move the scroll if the selection is not visible
-        int indexPosition = mHeightCell*index;
-        int height = mList.getHeight();
-        
-        if (indexPosition > height) {
-            if (android.os.Build.VERSION.SDK_INT >= 11)
-            {
-                mList.smoothScrollToPosition(index); 
-            }
-            else if (android.os.Build.VERSION.SDK_INT >= 8)
-            {
-                mList.setSelectionFromTop(index, 0);
-            }
+        if (mIndexes.size() > 0) {  
+            // needs to be checked; not every browse-up had a browse-down before 
+            
+            int index = mIndexes.remove(mIndexes.size() - 1);
             
             
+            int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
+            
+            int top = mTops.remove(mTops.size() - 1);
+            
+            mList.setSelectionFromTop(firstPosition, top);
+            
+            // Move the scroll if the selection is not visible
+            int indexPosition = mHeightCell*index;
+            int height = mList.getHeight();
+            
+            if (indexPosition > height) {
+                if (android.os.Build.VERSION.SDK_INT >= 11)
+                {
+                    mList.smoothScrollToPosition(index); 
+                }
+                else if (android.os.Build.VERSION.SDK_INT >= 8)
+                {
+                    mList.setSelectionFromTop(index, 0);
+                }
+                
+            }
         }
         }
     }
     }