Skip to content

Commit

Permalink
Fixed: Fix image resizing by regenerating image files
Browse files Browse the repository at this point in the history
Compress to jpeg and store original image to `TERMUX_BACKGROUND_DIR`.

Use `Activity` size instead of display size.

If portrait and landcape files are deleted or `Activity` size is changed, then regenerate images using original image.
  • Loading branch information
Kruna1Pate1 committed Nov 7, 2022
1 parent ecd1b84 commit 4be45f6
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 50 deletions.
Expand Up @@ -45,7 +45,7 @@ private void configureBackgroundPreferences(@NonNull Context context) {

// If background image preference is disabled and background images are
// missing, then don't allow user to enable it from setting.
if (!preferences.isBackgroundImageEnabled() && !TermuxBackgroundManager.isImageFilesExist(context)) {
if (!preferences.isBackgroundImageEnabled() && !TermuxBackgroundManager.isImageFilesExist(context, false)) {
backgroundImagePreference.setEnabled(false);
}
}
Expand Down
139 changes: 91 additions & 48 deletions app/src/main/java/com/termux/app/style/TermuxBackgroundManager.java
@@ -1,12 +1,14 @@
package com.termux.app.style;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.os.Handler;
import android.os.Looper;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
Expand All @@ -31,6 +33,9 @@
import com.termux.terminal.TerminalSession;
import com.termux.terminal.TextStyle;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TermuxBackgroundManager {

/** Require {@link TermuxActivity} to perform operations. */
Expand All @@ -42,13 +47,21 @@ public class TermuxBackgroundManager {
/** Termux app shared preferences manager. */
private final TermuxAppSharedPreferences mPreferences;

/** ExecutorService to execute task in bagdround. */
private final ExecutorService executor;

/** Handler allows to send and process {@link android.os.Message Message}. */
private final Handler handler;


private static final String LOG_TAG = "TermuxBackgroundManager";

public TermuxBackgroundManager(TermuxActivity activity) {
this.mActivity = activity;
this.mPreferences = activity.getPreferences();
this.mActivityResultLauncher = registerActivityResultLauncher();
this.executor = Executors.newSingleThreadExecutor();
this.handler = new Handler(Looper.getMainLooper());
}

/**
Expand All @@ -62,36 +75,29 @@ private ActivityResultLauncher<String> registerActivityResultLauncher() {

if (uri != null) {
try {
new Thread(() -> {
executor.execute(() -> {
Bitmap bitmap = ImageUtils.getBitmap(mActivity, uri);

if (bitmap == null) {
Logger.logErrorAndShowToast(mActivity, LOG_TAG, mActivity.getString(R.string.error_background_image_loading_from_gallery_failed));
return;
}

Point size = ViewUtils.getDisplaySize(mActivity, false);
boolean isLandscape = ViewUtils.getDisplayOrientation(mActivity) == Configuration.ORIENTATION_LANDSCAPE;
Error error;
ImageUtils.compressAndSaveBitmap(bitmap, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
boolean success = generateImageFiles(mActivity, bitmap);

if (isLandscape) {
error = ImageUtils.saveForDisplayResolution(bitmap, size, TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
if (success) {
notifyBackgroundUpdated(true);

} else {
error = ImageUtils.saveForDisplayResolution(bitmap, DataUtils.swap(size), TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
}
Logger.logInfo(LOG_TAG, "Image received successfully from the gallary.");
Logger.logDebug(LOG_TAG, "Storing background original image to " + TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
Logger.logDebug(LOG_TAG, "Storing background portrait image to " + TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH);
Logger.logDebug(LOG_TAG, "Storing background landscape image to " + TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH);

if (error != null) {
} else {
Logger.logErrorAndShowToast(mActivity, LOG_TAG, mActivity.getString(R.string.error_background_image_loading_from_gallery_failed));
return;
}

notifyBackgroundUpdated(true);

Logger.logInfo(LOG_TAG, "Image received successfully from the gallary.");
Logger.logDebug(LOG_TAG, "Storing background portrait image to " + TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
Logger.logDebug(LOG_TAG, "Storing background landscape image to " + TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH);
}).start();
});

} catch (Exception e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to load image", e);
Expand All @@ -108,15 +114,23 @@ private ActivityResultLauncher<String> registerActivityResultLauncher() {
* @param context The context for operation.
* @return Returns whether the optimized background image exist or not.
*/
public static boolean isImageFilesExist(@NonNull Context context) {
public static boolean isImageFilesExist(@NonNull Context context, boolean shouldGenerate) {
boolean isLandscape = (ViewUtils.getDisplayOrientation(context) == Configuration.ORIENTATION_LANDSCAPE);
Point size = ViewUtils.getDisplaySize(context, false);
Point size = ViewUtils.getDisplaySize(context, true);

String imagePath1 = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH;

String imagePath1 = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH;
String imagePath2 = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH;

String imagePath2 = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH;
boolean exist = ImageUtils.isImageOptimized(imagePath1, size)
&& ImageUtils.isImageOptimized(imagePath2, DataUtils.swap(size));

if (!exist && shouldGenerate && ImageUtils.isImage(TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH)) {
Bitmap bitmap = ImageUtils.getBitmap(TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH);
return generateImageFiles(context, bitmap);
}

return ImageUtils.isImageOptimized(imagePath1, size) && ImageUtils.isImageOptimized(imagePath2, DataUtils.swap(size));
return exist;
}


Expand All @@ -125,7 +139,7 @@ public static boolean isImageFilesExist(@NonNull Context context) {
* Enable background image loading. If the image already exist then ask for restore otherwise pick from gallery.
*/
public void setBackgroundImage() {
if (!mPreferences.isBackgroundImageEnabled() && isImageFilesExist(mActivity)) {
if (!mPreferences.isBackgroundImageEnabled() && isImageFilesExist(mActivity, true)) {
restoreBackgroundImages();

} else {
Expand All @@ -142,8 +156,7 @@ public void setBackgroundImage() {
*/
public void removeBackgroundImage(boolean deleteFiles) {
if (deleteFiles) {
FileUtils.deleteRegularFile(null, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH, true);
FileUtils.deleteRegularFile(null, TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH, true);
FileUtils.deleteDirectoryFile(null, TermuxConstants.TERMUX_BACKGROUND_DIR_PATH, true);
}

notifyBackgroundUpdated(false);
Expand Down Expand Up @@ -173,6 +186,35 @@ private void restoreBackgroundImages() {
b.show();
}

/**
* Generate background image files using original image. {@link Context}
* passed to this method must be of an {@link Activity} to determine the size
* of display.
*
* @param context The context require for the operations.
* @param bitmap Image bitmap to save as background.
* @return Returns whether the images generated successfully.
*/
public static boolean generateImageFiles(@NonNull Context context, Bitmap bitmap) {

if (bitmap == null || !(context instanceof Activity)) {
return false;
}

Point size = ViewUtils.getDisplaySize(context, true);
boolean isLandscape = ViewUtils.getDisplayOrientation(context) == Configuration.ORIENTATION_LANDSCAPE;
Error error;

if (isLandscape) {
error = ImageUtils.saveForDisplayResolution(bitmap, size, TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH);

} else {
error = ImageUtils.saveForDisplayResolution(bitmap, DataUtils.swap(size), TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH, TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH);
}

return error == null;
}


/**
* Updates background to image or solid color. If forced then load again even if
Expand All @@ -184,7 +226,6 @@ private void restoreBackgroundImages() {
public void updateBackground(boolean forced) {
if (!mActivity.isVisible()) return;

Log.d("FF", " " + mActivity.getPreferences().isBackgroundImageEnabled());
if (mActivity.getPreferences().isBackgroundImageEnabled()) {

Drawable drawable = mActivity.getWindow().getDecorView().getBackground();
Expand Down Expand Up @@ -221,28 +262,30 @@ public void updateBackgroundColor() {
*/
public void updateBackgroundImage() {
boolean isLandscape = ViewUtils.getDisplayOrientation(mActivity) == Configuration.ORIENTATION_LANDSCAPE;
String imagePath = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_PORTRAIT_PATH;

try {
// Performing on main Thread may cause ANR and lag.
executor.execute(() -> {
if (isImageFilesExist(mActivity, true)) {
Drawable drawable = ImageUtils.getDrawable(imagePath);
ImageUtils.addOverlay(drawable, mActivity.getProperties().getBackgroundOverlayColor());

handler.post(() -> mActivity.getWindow().getDecorView().setBackground(drawable));
} else {
Logger.logErrorAndShowToast(mActivity, LOG_TAG, mActivity.getString(R.string.error_background_image_loading_failed));

// Image files are unable to load so set background to solid color and notify update.
handler.post(this::updateBackgroundColor);
notifyBackgroundUpdated(false);
}
});

String imagePath = isLandscape ? TermuxConstants.TERMUX_BACKGROUND_IMAGE_LANDSCAPE_PATH : TermuxConstants.TERMUX_BACKGROUND_IMAGE_PATH;

if (isImageFilesExist(mActivity)) {
try {
Drawable drawable = ImageUtils.getDrawable(imagePath);
ImageUtils.addOverlay(drawable, mActivity.getProperties().getBackgroundOverlayColor());
mActivity.getWindow().getDecorView().setBackground(drawable);

} catch (Exception e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to load image", e);
Logger.showToast(mActivity, mActivity.getString(R.string.error_background_image_loading_from_gallery_failed), true);

// Since loading of image is failed, Set background to solid color.
updateBackgroundColor();
notifyBackgroundUpdated(false);
}

} else {
Logger.logErrorAndShowToast(mActivity, LOG_TAG, mActivity.getString(R.string.error_background_image_loading_failed));
} catch (Exception e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to load image", e);
Logger.showToast(mActivity, mActivity.getString(R.string.error_background_image_loading_failed), true);

// Image files are unable to load so set background to solid color and notify update.
// Since loading of image is failed, Set background to solid color.
updateBackgroundColor();
notifyBackgroundUpdated(false);
}
Expand Down
Expand Up @@ -80,6 +80,18 @@ public static Bitmap getBitmap(final Context context, Uri uri) {
return bitmap;
}

/**
* Generate image bitmap from the path.
*
* @param path The path for image file
* @return Bitmap generated from image path, if fails to
* to generate returns {@code null}
*/
public static Bitmap getBitmap(String path) {
return BitmapFactory.decodeFile(path);
}


/**
* Creates an centered and resized {@link Bitmap} according to given size.
*
Expand Down Expand Up @@ -201,15 +213,28 @@ public static boolean isImageOptimized(String path, int width, int height, int t
}

BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opt);

opt.inJustDecodeBounds = true;
int imgWidth = opt.outWidth;
int imgHeight = opt.outHeight;

return Math.abs(imgWidth - width) <= tolerance && Math.abs(imgHeight - height) <= tolerance;
}

public static boolean isImage(String path) {
if (!FileUtils.regularFileExists(path, false)) {
Logger.logInfo(LOG_TAG, "Image file " + path + " does not exist.");
return false;
}

BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opt);

return opt.outWidth != -1 && opt.outHeight != -1;
}

/**
* Resize bitmap for {@link Configuration#ORIENTATION_PORTRAIT Portrait} and {@link
* Configuration#ORIENTATION_LANDSCAPE Landscape} display view.
Expand Down

0 comments on commit 4be45f6

Please sign in to comment.