User chooser for sending mail in LogHistory (with crash fixed if device is rotated in the e-mail app)
--- /dev/null
+<?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
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
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;
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);
mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads
}
}
-
+
}
+++ /dev/null
-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;
- }
-}
--- /dev/null
+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;
+ }
+}
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;
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);
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));
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);
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;
private static final String TAG = PreviewImageFragment.class.getSimpleName();
private boolean mIgnoreFirstSavedState;
+
+ private LoadBitmapTask mLoadBitmapTask = null;
/**
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}
*/
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.
*
* @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);
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) {
showErrorMessage();
}
}
-
+
@SuppressLint("InlinedApi")
private void showLoadedImage(Bitmap result) {
if (mImageViewRef != null) {
*/
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.
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;
+ }
+
+
}