Merge pull request #781 from owncloud/fix_logs_change_orientation
authorDavid A. Velasco <dvelasco@owncloud.com>
Thu, 11 Dec 2014 09:21:41 +0000 (10:21 +0100)
committerDavid A. Velasco <dvelasco@owncloud.com>
Thu, 11 Dec 2014 09:21:41 +0000 (10:21 +0100)
User chooser for sending mail in LogHistory (with crash fixed if device is rotated in the e-mail app)

15 files changed:
res/layout/preference_widget_radiobutton.xml [new file with mode: 0644]
res/values-de-rDE/strings.xml
res/values-de/strings.xml
res/values-it/strings.xml
res/values-ja-rJP/strings.xml
res/values-nl/strings.xml
res/values-ru/strings.xml
res/values-sl/strings.xml
res/values-tr/strings.xml
src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
src/com/owncloud/android/ui/LongClickableCheckBoxPreference.java [deleted file]
src/com/owncloud/android/ui/RadioButtonPreference.java [new file with mode: 0644]
src/com/owncloud/android/ui/activity/Preferences.java
src/com/owncloud/android/ui/preview/PreviewImageFragment.java
src/com/owncloud/android/utils/BitmapUtils.java

diff --git a/res/layout/preference_widget_radiobutton.xml b/res/layout/preference_widget_radiobutton.xml
new file mode 100644 (file)
index 0000000..85f8f60
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!-- Layout used by CheckBoxPreference for the checkbox style. This is inflated
+inside android.R.layout.preference. -->
+<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
+android:id="@+android:id/checkbox"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:layout_gravity="center"
+android:focusable="false"
+android:clickable="false" />
\ No newline at end of file
index dca4302..00fdce2 100644 (file)
   <string name="forbidden_permissions_move">um diese Datei zu verschieben</string>
   <string name="prefs_category_instant_uploading">Sofortiges Hochladen</string>
   <string name="prefs_category_security">Sicherheit</string>
-  <string name="prefs_instant_video_upload_path_title">Upload-Verzeichnis für Videos</string>
+  <string name="prefs_instant_video_upload_path_title">Verzeichnis zum Hochladen der Videos</string>
 </resources>
index bfc86ce..2d7ccd4 100644 (file)
   <string name="forbidden_permissions_move">um diese Datei zu verschieben</string>
   <string name="prefs_category_instant_uploading">Sofortiges Hochladen</string>
   <string name="prefs_category_security">Sicherheit</string>
+  <string name="prefs_instant_video_upload_path_title">Verzeichnis zum Hochladen der Videos</string>
 </resources>
index ea3aa0a..4efe1ef 100644 (file)
@@ -35,7 +35,7 @@
   <string name="prefs_log_summary_history">Mostra i log registrati</string>
   <string name="prefs_log_delete_history_button">Elimina la cronologia</string>
   <string name="prefs_help">Aiuto</string>
-  <string name="prefs_recommend">Consiglia ad un amico</string>
+  <string name="prefs_recommend">Consiglia a un amico</string>
   <string name="prefs_feedback">Segnalazioni</string>
   <string name="prefs_imprint">Imprint</string>
   <string name="prefs_remember_last_share_location">Ricorda la posizione della condivisione</string>
   <string name="forbidden_permissions_move">per spostare questo file</string>
   <string name="prefs_category_instant_uploading">Caricamenti istantanei</string>
   <string name="prefs_category_security">Protezione</string>
+  <string name="prefs_instant_video_upload_path_title">Percorso di caricamento video</string>
 </resources>
index a3c0452..7c0d99c 100644 (file)
   <string name="forbidden_permissions_move">このファイルを移動</string>
   <string name="prefs_category_instant_uploading">自動アップロード</string>
   <string name="prefs_category_security">セキュリティ</string>
+  <string name="prefs_instant_video_upload_path_title">動画のアップロードパス</string>
 </resources>
index 600e182..84a0e0d 100644 (file)
@@ -299,4 +299,5 @@ Hieronder staan de lokale bestanden en de externe bestanden in %5$s waar ze naar
   <string name="forbidden_permissions_move">om dit bestand te verplaatsen</string>
   <string name="prefs_category_instant_uploading">Directe uploads</string>
   <string name="prefs_category_security">Beveiliging</string>
+  <string name="prefs_instant_video_upload_path_title">Upload Video Pad</string>
 </resources>
index 8a62801..3f58746 100644 (file)
@@ -38,6 +38,8 @@
   <string name="prefs_recommend">Рекомендовать другу</string>
   <string name="prefs_feedback">Обратная связь</string>
   <string name="prefs_imprint">Штамп</string>
+  <string name="prefs_remember_last_share_location">Запомнить расположение публикации</string>
+  <string name="prefs_remember_last_upload_location_summary">Запомнить расположение загрузки последней публикации</string>
   <string name="recommend_subject">Попробуйте %1$s на вашем смартфоне!</string>
   <string name="recommend_text">Хочу предложить вам использовать %1$s на смартфоне!\nЗагрузить можно здесь: %2$s
        </string>
   <string name="forbidden_permissions_move">переместить этот файл</string>
   <string name="prefs_category_instant_uploading">Мгновенные загрузки</string>
   <string name="prefs_category_security">Безопасность</string>
+  <string name="prefs_instant_video_upload_path_title">Путь для загрузки Видео</string>
 </resources>
index b283b27..86be0f3 100644 (file)
   <string name="forbidden_permissions_move">med premikanjem datoteke</string>
   <string name="prefs_category_instant_uploading">Takojšnje pošiljanje v oblak</string>
   <string name="prefs_category_security">Varnost</string>
+  <string name="prefs_instant_video_upload_path_title">Pot videa za pošiljanje</string>
 </resources>
index ec1c6a7..a35922f 100644 (file)
   <string name="forbidden_permissions_move">bu dosyayı taşımak için</string>
   <string name="prefs_category_instant_uploading">Anında Yüklemeler</string>
   <string name="prefs_category_security">Güvenlik</string>
+  <string name="prefs_instant_video_upload_path_title">Video Yükleme Yolu</string>
 </resources>
index 1d2cda8..ce53c44 100644 (file)
@@ -29,8 +29,10 @@ import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.media.ExifInterface;
 import android.media.ThumbnailUtils;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -216,6 +218,9 @@ public class ThumbnailsCacheManager {
                         
                         if (bitmap != null) {
                             thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+                            
+                            // Rotate image, obeying exif tag
+                            thumbnail = BitmapUtils.rotateImage(thumbnail, mFile.getStoragePath());
     
                             // Add thumbnail to cache
                             addBitmapToCache(imageKey, thumbnail);
@@ -317,5 +322,5 @@ public class ThumbnailsCacheManager {
             mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads
         }
     }
-
+    
 }
diff --git a/src/com/owncloud/android/ui/LongClickableCheckBoxPreference.java b/src/com/owncloud/android/ui/LongClickableCheckBoxPreference.java
deleted file mode 100644 (file)
index 5befe64..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.owncloud.android.ui;
-
-import android.content.Context;
-import android.preference.CheckBoxPreference;
-import android.view.View;
-
-public class LongClickableCheckBoxPreference extends CheckBoxPreference implements View.OnLongClickListener {
-
-    public LongClickableCheckBoxPreference(Context context) {
-        super(context);
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        return true;
-    }
-}
diff --git a/src/com/owncloud/android/ui/RadioButtonPreference.java b/src/com/owncloud/android/ui/RadioButtonPreference.java
new file mode 100644 (file)
index 0000000..8f562b3
--- /dev/null
@@ -0,0 +1,20 @@
+package com.owncloud.android.ui;
+
+import android.content.Context;
+import android.preference.CheckBoxPreference;
+import android.view.View;
+
+import com.owncloud.android.R;
+
+public class RadioButtonPreference extends CheckBoxPreference implements View.OnLongClickListener {
+    
+    public RadioButtonPreference(Context context) {
+        super(context, null, android.R.attr.checkBoxPreferenceStyle);
+        setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
+    }
+  
+    @Override
+    public boolean onLongClick(View v) {
+        return true;
+    }
+}
index f794cf1..04a4c7a 100644 (file)
@@ -53,7 +53,7 @@ import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.db.DbHandler;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.ui.LongClickableCheckBoxPreference;
+import com.owncloud.android.ui.RadioButtonPreference;
 import com.owncloud.android.utils.DisplayUtils;
 
 
@@ -107,9 +107,9 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
                 ListAdapter listAdapter = listView.getAdapter();
                 Object obj = listAdapter.getItem(position);
 
-                if (obj != null && obj instanceof LongClickableCheckBoxPreference) {
+                if (obj != null && obj instanceof RadioButtonPreference) {
                     mShowContextMenu = true;
-                    mAccountName = ((LongClickableCheckBoxPreference) obj).getKey();
+                    mAccountName = ((RadioButtonPreference) obj).getKey();
 
                     Preferences.this.openContextMenu(listView);
 
@@ -463,7 +463,7 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
         else {
 
             for (Account a : accounts) {
-                LongClickableCheckBoxPreference accountPreference = new LongClickableCheckBoxPreference(this);
+                RadioButtonPreference accountPreference = new RadioButtonPreference(this);
                 accountPreference.setKey(a.name);
                 // Handle internationalized domain names
                 accountPreference.setTitle(DisplayUtils.convertIdn(a.name, false));
@@ -483,7 +483,7 @@ public class Preferences extends SherlockPreferenceActivity implements AccountMa
                         AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
                         Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
                         for (Account a : accounts) {
-                            CheckBoxPreference p = (CheckBoxPreference) findPreference(a.name);
+                            RadioButtonPreference p = (RadioButtonPreference) findPreference(a.name);
                             if (key.equals(a.name)) {
                                 boolean accountChanged = !p.isChecked(); 
                                 p.setChecked(true);
index a3814c7..0995793 100644 (file)
@@ -53,6 +53,7 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.utils.BitmapUtils;
 import com.owncloud.android.utils.TouchImageViewCustom;
 
 
@@ -82,6 +83,8 @@ public class PreviewImageFragment extends FileFragment {
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
 
     private boolean mIgnoreFirstSavedState;
+    
+    private LoadBitmapTask mLoadBitmapTask = null;
 
     
     /**
@@ -190,12 +193,22 @@ public class PreviewImageFragment extends FileFragment {
     public void onStart() {
         super.onStart();
         if (getFile() != null) {
-           BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
-           bl.execute(new String[]{getFile().getStoragePath()});
+           mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel);
+           mLoadBitmapTask.execute(new String[]{getFile().getStoragePath()});
         }
     }
     
     
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mLoadBitmapTask != null) {
+            mLoadBitmapTask.cancel(true);
+            mLoadBitmapTask = null;
+        }
+        
+    }
+    
     /**
      * {@inheritDoc}
      */
@@ -328,8 +341,8 @@ public class PreviewImageFragment extends FileFragment {
         finish();
     }
     
-
-    private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
+    
+    private class LoadBitmapTask extends AsyncTask<String, Void, Bitmap> {
 
         /**
          * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
@@ -365,7 +378,7 @@ public class PreviewImageFragment extends FileFragment {
          * 
          * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
          */
-        public BitmapLoader(ImageViewCustom imageView, TextView messageView, ProgressBar progressWheel) {
+        public LoadBitmapTask(ImageViewCustom imageView, TextView messageView, ProgressBar progressWheel) {
             mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
             mMessageViewRef = new WeakReference<TextView>(messageView);
             mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
@@ -379,43 +392,65 @@ public class PreviewImageFragment extends FileFragment {
             String storagePath = params[0];
             try {
 
+                if (isCancelled()) return result;
+                
                 File picture = new File(storagePath);
 
                 if (picture != null) {
-                    //Decode file into a bitmap in real size for being able to make zoom on the image
+                    // Decode file into a bitmap in real size for being able to make zoom on 
+                    // the image
                     result = BitmapFactory.decodeStream(new FlushedInputStream
                             (new BufferedInputStream(new FileInputStream(picture))));
                 }
 
+                if (isCancelled()) return result;
+                
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                } else {
+                    // Rotate image, obeying exif tag.
+                    result = BitmapUtils.rotateImage(result, storagePath);
                 }
                 
             } catch (OutOfMemoryError e) {
                 Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
 
-                // If out of memory error when loading image, try to load it scaled
+                if (isCancelled()) return result;
+                
+                // If out of memory error when loading or rotating image, try to load it scaled
                 result = loadScaledImage(storagePath);
 
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                } else {
+                    // Rotate scaled image, obeying exif tag
+                    result = BitmapUtils.rotateImage(result, storagePath);
                 }
                     
             } catch (NoSuchFieldError e) {
                 mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " + storagePath, e);
+                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " 
+                                + storagePath, e);
                     
             } catch (Throwable t) {
                 mErrorMessageId = R.string.common_error_unknown;
                 Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
                 
             }
+            
             return result;
         }
         
         @Override
+        protected void onCancelled(Bitmap result) {
+            if (result != null) {
+                result.recycle();
+            }
+        }
+
+        @Override
         protected void onPostExecute(Bitmap result) {
             hideProgressWheel();
             if (result != null) {
@@ -424,7 +459,7 @@ public class PreviewImageFragment extends FileFragment {
                 showErrorMessage();
             }
         }
-
+        
         @SuppressLint("InlinedApi")
         private void showLoadedImage(Bitmap result) {
             if (mImageViewRef != null) {
index 687b5a4..7036727 100644 (file)
  */
 package com.owncloud.android.utils;
 
+import com.owncloud.android.lib.common.utils.Log_OC;
+
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
 import android.graphics.BitmapFactory.Options;
+import android.media.ExifInterface;
 
 /**
  * Utility class with methods for decoding Bitmaps.
@@ -96,4 +100,75 @@ public class BitmapUtils {
         return inSampleSize;
     }
     
+    /**
+     * Rotate bitmap according to EXIF orientation. 
+     * Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ 
+     * @param bitmap Bitmap to be rotated
+     * @param storagePath Path to source file of bitmap. Needed for EXIF information. 
+     * @return correctly EXIF-rotated bitmap
+     */
+    public static Bitmap rotateImage(Bitmap bitmap, String storagePath){
+        Bitmap resultBitmap = bitmap;
+
+        try
+        {
+            ExifInterface exifInterface = new ExifInterface(storagePath);
+            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
+
+            Matrix matrix = new Matrix();
+
+            // 1: nothing to do
+            
+            // 2
+            if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL)
+            {
+                matrix.postScale(-1.0f, 1.0f);
+            }
+            // 3
+            else if (orientation == ExifInterface.ORIENTATION_ROTATE_180)
+            {
+                matrix.postRotate(180);
+            }
+            // 4
+            else if (orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL)
+            {
+                matrix.postScale(1.0f, -1.0f);
+            }
+            // 5
+            else if (orientation == ExifInterface.ORIENTATION_TRANSPOSE)
+            {
+                matrix.postRotate(-90);
+                matrix.postScale(1.0f, -1.0f);
+            }
+            // 6
+            else if (orientation == ExifInterface.ORIENTATION_ROTATE_90)
+            {
+                matrix.postRotate(90);
+            }
+            // 7
+            else if (orientation == ExifInterface.ORIENTATION_TRANSVERSE)
+            {
+                matrix.postRotate(90);
+                matrix.postScale(1.0f, -1.0f);
+            }
+            // 8
+            else if (orientation == ExifInterface.ORIENTATION_ROTATE_270)
+            {
+                matrix.postRotate(270);
+            } 
+            
+            // Rotate the bitmap
+            resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
+            if (resultBitmap != bitmap) {
+                bitmap.recycle();
+            }
+        }
+        catch (Exception exception)
+        {
+            Log_OC.e("BitmapUtil", "Could not rotate the image: " + storagePath);
+        }
+        return resultBitmap;
+    }
+    
+    
 }