Skip to content

Commit

Permalink
Enable image display in notes
Browse files Browse the repository at this point in the history
- Background load images with glide
- Add new settings:
  - Enable image display
  - Set fixed size image or enable scaling
  - Set fixed size for images
- Add function to process input paths and drawables
  • Loading branch information
aancel committed Oct 22, 2018
1 parent 813f4f9 commit 1e6a95a
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 6 deletions.
7 changes: 7 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,10 @@ def orgJavaLocation() {
return "com.orgzly:org-java:$org_java_version"
}
}

apply plugin: 'kotlin-kapt'

dependencies {
implementation 'com.github.bumptech.glide:glide:4.8.0'
kapt 'com.github.bumptech.glide:compiler:4.8.0'
}
31 changes: 31 additions & 0 deletions app/src/main/java/com/orgzly/android/prefs/AppPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,37 @@ public static void setLastRepeatOnTimeShift(Context context, boolean value) {
getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}

/*
* Allow inlining images
*/
public static boolean displayInlineImages(Context context) {
return getDefaultSharedPreferences(context).getBoolean(
context.getResources().getString(R.string.pref_key_display_inline_images),
context.getResources().getBoolean(R.bool.pref_default_display_inline_images));
}

public static void displayInlineImages(Context context, boolean value) {
String key = context.getResources().getString(R.string.pref_key_display_inline_images);
getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}

public static boolean enableImageScaling(Context context) {
return getDefaultSharedPreferences(context).getBoolean(
context.getResources().getString(R.string.pref_key_enable_image_scaling),
context.getResources().getBoolean(R.bool.pref_default_enable_image_scaling));
}

public static void enableImageScaling(Context context, boolean value) {
String key = context.getResources().getString(R.string.pref_key_enable_image_scaling);
getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply();
}

public static int setImageFixedWidth(Context context) {
return Integer.valueOf(getDefaultSharedPreferences(context).getString(
context.getResources().getString(R.string.pref_key_set_image_fixed_width),
context.getResources().getString(R.string.pref_default_set_image_fixed_width)));
}

/*
* Note's metadata visibility
*/
Expand Down
127 changes: 121 additions & 6 deletions app/src/main/java/com/orgzly/android/ui/ImageLoader.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
package com.orgzly.android.ui

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Environment
import android.support.v4.content.FileProvider
import android.text.Spannable
import android.text.style.ImageSpan
import android.view.View
import com.orgzly.BuildConfig
import com.orgzly.android.App
import com.orgzly.android.prefs.AppPreferences
import com.orgzly.android.ui.views.TextViewWithMarkup
import com.orgzly.android.ui.views.style.FileLinkSpan
import com.orgzly.android.util.AppPermissions
import java.io.File
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.bumptech.glide.request.RequestOptions


object ImageLoader {
Expand All @@ -11,17 +27,116 @@ object ImageLoader {
val context = textWithMarkup.context

// Only if AppPreferences.displayImages(context) is true
// loadImages(textWithMarkup.text as Spannable)
// Setup image visualization inside the note
if ( AppPreferences.displayInlineImages(context)
// Storage permission has been granted
&& AppPermissions.isGranted(context, AppPermissions.Usage.EXTERNAL_FILES_ACCESS)) {
// Load the associated image for each FileLinkSpan
SpanUtils.forEachSpan(textWithMarkup.text as Spannable, FileLinkSpan::class.java) { span ->
loadImage(textWithMarkup, span)
}
}
}

private fun loadImages(text: Spannable) {
SpanUtils.forEachSpan(text, FileLinkSpan::class.java) { span ->
loadImage(span)
private fun loadImage(textWithMarkup: TextViewWithMarkup, span: FileLinkSpan) {
val path = span.path

if (hasSupportedExtension(path)) {
val text = textWithMarkup.text as Spannable
// Get the current context
val context = App.getAppContext()

// Get the file
val file = File(Environment.getExternalStorageDirectory(), path)

if(file.exists()) {
// Get the Uri
val contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file)

// Setup a placeholder
val drawable = ColorDrawable(Color.TRANSPARENT)
drawable.setBounds(0, 0, AppPreferences.setImageFixedWidth(context), AppPreferences.setImageFixedWidth(context))

Glide.with(context)
.asDrawable()
// Use a placeholder
.apply(RequestOptions().placeholder(drawable))
// And scaled thumbnails for faster display
.thumbnail(Glide.with(context)
.applyDefaultRequestOptions(RequestOptions().override(AppPreferences.setImageFixedWidth(context)))
.load(contentUri))
.load(contentUri)
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
fitDrawable(textWithMarkup, resource)
text.setSpan(ImageSpan(resource), text.getSpanStart(span), text.getSpanEnd(span), text.getSpanFlags(span))
}
})
}
}
}

private fun loadImage(span: FileLinkSpan) {
val path = span.path
fun hasSupportedExtension(path: String): Boolean {
var ret = false
var index: Int
var s = ""

// Find the last slash in the path
// to avoid case of a point in the path and a file without extension
index = path.lastIndexOf("/")

// If we found the last slash, extract the file name
if (index != -1) {
s = path.substring(index + 1)
}

// Extract the extension
index = s.lastIndexOf(".")

// If we found an extension, extract it and test it
if (index != -1) {
s = s.substring(index + 1).toLowerCase()

if (s == "jpg" || s == "jpeg" || s == "gif"
|| s == "png" || s == "bmp" || s == "webp") {
ret = true
}
}

return ret
}

fun fitDrawable(view: View, drawable: Drawable) {
// Get the display metrics to be able to rescale the image if needed
val metrics = view.context.resources.displayMetrics

// Gather drawable information
// Scale the height by the scaledDensity to get original image size
val drawableHeight = drawable.intrinsicHeight.toFloat() * metrics.scaledDensity
val drawableWidth = drawable.intrinsicWidth.toFloat() * metrics.scaledDensity

// Use either a fixed size or a scaled size according to user preferences
var fixedSize = -1
if (!AppPreferences.enableImageScaling(view.context)) {
fixedSize = AppPreferences.setImageFixedWidth(view.context)
}

if (fixedSize > 0) {
// Keep aspect ratio when using fixed size
val ratio = drawableHeight / drawableWidth
drawable.setBounds(0, 0, fixedSize, (fixedSize * ratio).toInt())
} else {
// Rescale the drawable if it is larger that the current view width
if (drawableWidth > view.width) {
//Compute image ratio
val ratio = drawableHeight / drawableWidth
// Ensure that the images have a minimum size
val width = Math.max(view.width, 256).toFloat()

drawable.setBounds(0, 0, width.toInt(), (width * ratio).toInt())
} else {
drawable.setBounds(0, 0, drawableWidth.toInt(), drawableHeight.toInt())
}
}
}
}
9 changes: 9 additions & 0 deletions app/src/main/res/values/prefs_keys.xml
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@
<string name="pref_key_set_last_repeat_on_time_shift" translatable="false">pref_key_set_last_repeat_on_time_shift</string>
<bool name="pref_default_set_last_repeat_on_time_shift" translatable="false">true</bool>

<string name="pref_key_display_inline_images" translatable="false">pref_key_display_inline_images</string>
<bool name="pref_default_display_inline_images" translatable="false">true</bool>

<string name="pref_key_enable_image_scaling" translatable="false">pref_key_enable_image_scaling</string>
<bool name="pref_default_enable_image_scaling" translatable="false">true</bool>

<string name="pref_key_set_image_fixed_width" translatable="false">pref_key_set_image_fixed_width</string>
<string name="pref_default_set_image_fixed_width" translatable="false">128</string>

<!-- Separate notes with an empty line -->
<string name="pref_key_separate_notes_with_new_line" translatable="false">pref_key_separate_notes_with_new_line</string>
<string name="pref_default_separate_notes_with_new_line" translatable="false">@string/pref_value_separate_notes_with_new_line_multi_line_notes_only</string>
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -538,4 +538,11 @@

<string name="external_file_no_app_found">No application found to open this file</string>

<string name="display_inline_images">Display inline images</string>
<string name="display_inline_images_summary">Display inline images in notes</string>

<string name="enable_image_scaling">Enable image scaling</string>
<string name="enable_image_scaling_summary">Use predefined sizes or enable image scaling</string>

<string name="pref_title_set_image_fixed_width">Set inlined image fixed width (in pixels)</string>
</resources>
22 changes: 22 additions & 0 deletions app/src/main/res/xml/prefs_screen_notebooks.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/pref_title_notebooks">

<PreferenceCategory android:title="@string/notebooks">
Expand Down Expand Up @@ -148,6 +149,27 @@
android:summary="@string/set_last_repeat_on_time_shift_summary"
android:defaultValue="@bool/pref_default_set_last_repeat_on_time_shift"/>

<SwitchPreference
android:key="@string/pref_key_display_inline_images"
android:title="@string/display_inline_images"
android:summary="@string/display_inline_images_summary"
android:defaultValue="@bool/pref_default_display_inline_images"/>

<SwitchPreference
android:key="@string/pref_key_enable_image_scaling"
android:title="@string/enable_image_scaling"
android:summary="@string/enable_image_scaling_summary"
android:defaultValue="@bool/pref_default_enable_image_scaling"
android:dependency="@string/pref_key_display_inline_images"
android:disableDependentsState="true"/>

<com.orgzly.android.prefs.IntegerPreference
android:key="@string/pref_key_set_image_fixed_width"
android:title="@string/pref_title_set_image_fixed_width"
android:inputType="number"
android:defaultValue="@string/pref_default_set_image_fixed_width"
app:min="1"
android:dependency="@string/pref_key_enable_image_scaling"/>
</PreferenceCategory>

<PreferenceCategory android:title="@string/prefs_title_new_note">
Expand Down

0 comments on commit 1e6a95a

Please sign in to comment.