Can save some uploaded images

This commit is contained in:
Pierre HUBERT 2019-02-28 18:42:07 +01:00
parent 5f078867f2
commit 7ad41b1754
13 changed files with 218 additions and 11 deletions

View File

@ -13,6 +13,10 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- To save file (eg. images) into user storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup_scheme" android:fullBackupContent="@xml/backup_scheme"

View File

@ -7,6 +7,8 @@ import android.widget.ImageView;
import org.communiquons.android.comunic.client.data.runnables.ImageLoadRunnable; import org.communiquons.android.comunic.client.data.runnables.ImageLoadRunnable;
import java.util.Objects;
/** /**
* Image load manager / helper * Image load manager / helper
* *
@ -95,6 +97,20 @@ public class ImageLoadHelper {
clean(); clean();
} }
/**
* Check out whether an image view is loading or not
*
* @param v The target view
* @return TRUE if loading / FALSE else
*/
public static boolean IsLoading(View v){
return threads.containsKey(v)
&& threads.get(v) != null
&& Objects.requireNonNull(threads.get(v)).isAlive()
&& !Objects.requireNonNull(threads.get(v)).isInterrupted();
}
/** /**
* Clean the list of pending operations * Clean the list of pending operations
*/ */

View File

@ -28,12 +28,12 @@ public class ImageLoadRunnable implements Runnable {
/** /**
* Debug tag * Debug tag
*/ */
private static final String TAG = "ImageLoadRunnable"; private static final String TAG = ImageLoadRunnable.class.getSimpleName();
/** /**
* Image load lock * Image load lock
*/ */
public static ReentrantLock ImageLoadLock = null; private static ReentrantLock ImageLoadLock = null;
/** /**
* An array map with all the pending images associated with their URLs * An array map with all the pending images associated with their URLs
@ -89,11 +89,7 @@ public class ImageLoadRunnable implements Runnable {
//Create the parent directory if required //Create the parent directory if required
ImageLoadUtils.create_parent_directory(mContext); ImageLoadUtils.create_parent_directory(mContext);
//Determine the filename for the requested URL file = ImageLoadUtils.getFileForImage(mContext, url);
String filename = ImageLoadUtils.IMAGE_CACHE_DIRECTORY + ImageLoadUtils.get_file_name(url);
//Create file object
file = new File(mContext.getCacheDir(), filename);
//Check no thread is already running for the following image //Check no thread is already running for the following image
if (!pendingOperation.containsKey(url)) { if (!pendingOperation.containsKey(url)) {
@ -181,7 +177,8 @@ public class ImageLoadRunnable implements Runnable {
" deleted"); " deleted");
//Delete file //Delete file
file.delete(); if(!file.delete())
Log.e(TAG, "Could not delete file " + file.getPath() + " !");
return; return;
} }

View File

@ -22,7 +22,7 @@ public class ImageLoadUtils {
/** /**
* The main folder in the cache directory that stores the file * The main folder in the cache directory that stores the file
*/ */
public static final String IMAGE_CACHE_DIRECTORY = "img_cache/"; private static final String IMAGE_CACHE_DIRECTORY = "img_cache/";
/** /**
* Get the file name, based on the URL name * Get the file name, based on the URL name
@ -30,10 +30,28 @@ public class ImageLoadUtils {
* @param url The URL of the file * @param url The URL of the file
* @return The name of the file, composed of characters that can be used in filename * @return The name of the file, composed of characters that can be used in filename
*/ */
public static String get_file_name(String url){ private static String get_file_name(String url){
return StringsUtils.sha1(url); return StringsUtils.sha1(url);
} }
/**
* Get the File object corresponding to an image URL
*
* Warning ! In some cases, the file might not exists
*
* @param context Application context
* @param url URL of the target image
* @return File object pointing on the image
*/
public static File getFileForImage(Context context, String url){
//Determine the filename for the requested URL
String filename = IMAGE_CACHE_DIRECTORY + ImageLoadUtils.get_file_name(url);
//Create file object
return new File(context.getCacheDir(), filename);
}
/** /**
* Create cache images files parent directory if it does not exist * Create cache images files parent directory if it does not exist
* *

View File

@ -1,5 +1,6 @@
package org.communiquons.android.comunic.client.ui.activities; package org.communiquons.android.comunic.client.ui.activities;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@ -25,6 +26,7 @@ import android.widget.Toast;
import org.communiquons.android.comunic.client.BuildConfig; import org.communiquons.android.comunic.client.BuildConfig;
import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.R;
import org.communiquons.android.comunic.client.ui.utils.PermissionsUtils;
import org.communiquons.crashreporter.CrashReporter; import org.communiquons.crashreporter.CrashReporter;
import org.communiquons.android.comunic.client.data.enums.VirtualDirectoryType; import org.communiquons.android.comunic.client.data.enums.VirtualDirectoryType;
import org.communiquons.android.comunic.client.data.helpers.APIRequestHelper; import org.communiquons.android.comunic.client.data.helpers.APIRequestHelper;
@ -193,6 +195,11 @@ public class MainActivity extends BaseActivity implements
GetCallConfigurationTask callConfigurationTask = new GetCallConfigurationTask(this); GetCallConfigurationTask callConfigurationTask = new GetCallConfigurationTask(this);
callConfigurationTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); callConfigurationTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
getTasksManager().addTask(callConfigurationTask); getTasksManager().addTask(callConfigurationTask);
//Request a few permissions
PermissionsUtils.RequestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
} }
@Override @Override

View File

@ -91,6 +91,7 @@ class CommentsAdapter extends ArrayAdapter<Comment> {
//Update comment image (if any) //Update comment image (if any)
EnlargeableWebImageView commentImage = view.findViewById(R.id.comment_image); EnlargeableWebImageView commentImage = view.findViewById(R.id.comment_image);
commentImage.setCanSaveImageToGallery(true);
if(comment.getImage_url().length() < 5) if(comment.getImage_url().length() < 5)
commentImage.setVisibility(View.GONE); commentImage.setVisibility(View.GONE);
else { else {

View File

@ -136,6 +136,7 @@ public class ConversationMessageAdapter extends RecyclerView.Adapter {
mMessage.setOnLongClickListener(this); mMessage.setOnLongClickListener(this);
mImage.setOnLongClickListener(this); mImage.setOnLongClickListener(this);
mImage.setCanSaveImageToGallery(true);
} }
/** /**

View File

@ -415,6 +415,7 @@ public class PostsAdapter extends BaseRecyclerViewAdapter {
super(itemView); super(itemView);
mPostImage = new EnlargeableWebImageView(getContext()); mPostImage = new EnlargeableWebImageView(getContext());
mPostImage.setCanSaveImageToGallery(true);
getAdditionalViewsLayout().addView(mPostImage, getAdditionalViewsLayout().addView(mPostImage,
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
UiUtils.GetPixel(getContext(), 200))); UiUtils.GetPixel(getContext(), 200)));

View File

@ -0,0 +1,44 @@
package org.communiquons.android.comunic.client.ui.utils;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import java.util.ArrayList;
/**
* Permissions utilities
*
* @author Pierre HUBERT
*/
public class PermissionsUtils {
/**
* Request some permissions from the user
*
* @param activity Activity that perform the request
* @param permissions Requested permissions
*/
public static void RequestPermissions(@NonNull Activity activity, String[] permissions, int c){
ArrayList<String> permissionsToRequest = new ArrayList<>();
for(String permission : permissions){
if(ContextCompat.checkSelfPermission(activity, permission)
!= PackageManager.PERMISSION_GRANTED)
permissionsToRequest.add(permission);
}
//Ask for permissions if required
if(permissionsToRequest.size() > 0){
String[] permissionsArray = permissionsToRequest.toArray(new String[0]);
assert permissionsArray != null;
ActivityCompat.requestPermissions(activity, permissionsArray, c);
}
}
}

View File

@ -2,11 +2,22 @@ package org.communiquons.android.comunic.client.ui.views;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.PopupMenu;
import android.widget.Toast;
import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.R;
import org.communiquons.android.comunic.client.data.helpers.ImageLoadHelper;
import org.communiquons.android.comunic.client.data.utils.ImageLoadUtils;
import org.communiquons.android.comunic.client.ui.utils.BitmapUtils;
import java.io.File;
/** /**
* Enlargeable images view. * Enlargeable images view.
@ -17,13 +28,21 @@ import org.communiquons.android.comunic.client.R;
* Created by pierre on 4/28/18. * Created by pierre on 4/28/18.
*/ */
public class EnlargeableWebImageView extends WebImageView { public class EnlargeableWebImageView extends WebImageView implements PopupMenu.OnMenuItemClickListener {
/**
* Debug tag
*/
private static final String TAG = EnlargeableWebImageView.class.getSimpleName();
/** /**
* Optional additional OnClick Listener * Optional additional OnClick Listener
*/ */
private OnClickListener mOnClickListener; private OnClickListener mOnClickListener;
private boolean mCanSaveImageToGallery;
public EnlargeableWebImageView(Context context) { public EnlargeableWebImageView(Context context) {
super(context); super(context);
init(); init();
@ -68,6 +87,14 @@ public class EnlargeableWebImageView extends WebImageView {
return mOnClickListener == null; return mOnClickListener == null;
} }
public boolean isCanSaveImageToGallery() {
return mCanSaveImageToGallery;
}
public void setCanSaveImageToGallery(boolean mCanSaveImageToGallery) {
this.mCanSaveImageToGallery = mCanSaveImageToGallery;
}
/** /**
* Open the image in large dimensions * Open the image in large dimensions
*/ */
@ -86,5 +113,80 @@ public class EnlargeableWebImageView extends WebImageView {
.show(); .show();
//Offer the user to save if image if possible
if(isCanSaveImageToGallery()){
image.setOnLongClickListener(this::openContextMenu);
}
}
/**
* Open context menu for the image
*/
private boolean openContextMenu(View v){
//Show popup menu
PopupMenu popupMenu = new PopupMenu(getContext(), v);
popupMenu.inflate(R.menu.image_menu);
popupMenu.setOnMenuItemClickListener(this);
if(ImageLoadHelper.IsLoading(this))
popupMenu.getMenu().findItem(R.id.action_save_image_in_gallery).setEnabled(false);
popupMenu.show();
return true;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if(item.getItemId() == R.id.action_save_image_in_gallery){
if(!saveImageToGallery())
Toast.makeText(getContext(), R.string.err_save_image_in_gallery, Toast.LENGTH_SHORT).show();
else
Toast.makeText(getContext(), R.string.success_save_image_in_gallery, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
/**
* Save the current image in this {@link WebImageView} into the gallery
*
* @return TRUE in case of success / FALSE else
*/
private boolean saveImageToGallery(){
try {
if (ImageLoadHelper.IsLoading(this))
return false;
File file = ImageLoadUtils.getFileForImage(getContext(), getCurrURL());
if (!file.isFile()) {
Log.e(TAG, "Image is not a file!");
return false;
}
Bitmap bm = BitmapUtils.openResized(file, 1500, 1500);
//Save the image into the gallery
return MediaStore.Images.Media.insertImage(
getContext().getContentResolver(),
bm,
"Comunic image",
getCurrURL()
) != null;
} catch (Exception e){
e.printStackTrace();
return false;
}
} }
} }

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_save_image_in_gallery"
android:title="@string/action_save_image_in_gallery"
/>
</menu>

View File

@ -335,4 +335,7 @@
<string name="action_hang_up">Raccrocher</string> <string name="action_hang_up">Raccrocher</string>
<string name="action_switch_camera">Changer de caméra</string> <string name="action_switch_camera">Changer de caméra</string>
<string name="action_stop_camera">Arrêter la caméra</string> <string name="action_stop_camera">Arrêter la caméra</string>
<string name="action_save_image_in_gallery">Enregistrer dans la gallerie</string>
<string name="err_save_image_in_gallery">Une erreur a survenue lors de l\'enregistrement de l\'image dans la gallerie !</string>
<string name="success_save_image_in_gallery">L\'image a bien été enregistrée dans la gallerie !</string>
</resources> </resources>

View File

@ -334,4 +334,7 @@
<string name="action_hang_up">Hang up</string> <string name="action_hang_up">Hang up</string>
<string name="action_switch_camera">Switch camera</string> <string name="action_switch_camera">Switch camera</string>
<string name="action_stop_camera">Stop camera</string> <string name="action_stop_camera">Stop camera</string>
<string name="action_save_image_in_gallery">Save in gallery</string>
<string name="err_save_image_in_gallery">Could not save the image in the gallery!</string>
<string name="success_save_image_in_gallery">Successfully saved the image into the gallery!</string>
</resources> </resources>