layout fix, added asynctask for saving images, memory leak fix
This commit is contained in:
parent
8daf1fa1a5
commit
3b736b2d3c
@ -23,8 +23,10 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.ImageSaver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
@ -34,6 +36,7 @@ import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
import static android.content.Intent.ACTION_PICK;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.AsyncTask.Status.RUNNING;
|
||||
import static android.os.Environment.DIRECTORY_PICTURES;
|
||||
import static android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
@ -43,7 +46,7 @@ import static android.widget.Toast.LENGTH_SHORT;
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
abstract class MediaActivity extends AppCompatActivity implements LocationListener, Runnable {
|
||||
public abstract class MediaActivity extends AppCompatActivity implements LocationListener {
|
||||
|
||||
/**
|
||||
* permission set
|
||||
@ -96,14 +99,8 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
*/
|
||||
protected static final int REQUEST_STORE_IMG = 9;
|
||||
|
||||
/**
|
||||
* Quality of the saved jpeg images
|
||||
*/
|
||||
private static final int JPEG_QUALITY = 90;
|
||||
|
||||
@Nullable
|
||||
private LocationManager locationManager;
|
||||
private Thread imageSaveThread;
|
||||
private ImageSaver imageTask;
|
||||
private Bitmap image;
|
||||
private String filename;
|
||||
private boolean locationPending = false;
|
||||
@ -112,14 +109,20 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
@Override
|
||||
protected void onCreate(Bundle b) {
|
||||
super.onCreate(b);
|
||||
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (locationManager != null)
|
||||
locationManager.removeUpdates(this);
|
||||
if (locationPending) {
|
||||
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null) {
|
||||
locationManager.removeUpdates(this);
|
||||
}
|
||||
}
|
||||
if (imageTask != null && imageTask.getStatus() == RUNNING) {
|
||||
imageTask.cancel(true);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@ -136,8 +139,9 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
else
|
||||
onAttachLocation(null);
|
||||
} else if ((PERMISSIONS[2][0].equals(permissions[0]))) {
|
||||
if (grantResults[0] == PERMISSION_GRANTED)
|
||||
writeImageToStorage();
|
||||
if (grantResults[0] == PERMISSION_GRANTED) {
|
||||
saveImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,54 +165,36 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
boolean imageSaved = false;
|
||||
/**
|
||||
* save image to external storage
|
||||
*/
|
||||
private void saveImage() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
// use scoped storage
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
|
||||
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH, DIRECTORY_PICTURES);
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri imageUri = getContentResolver().insert(EXTERNAL_CONTENT_URI, values);
|
||||
if (imageUri != null) {
|
||||
OutputStream fileStream = getContentResolver().openOutputStream(imageUri);
|
||||
if (fileStream != null) {
|
||||
imageSaved = image.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, fileStream);
|
||||
fileStream.close();
|
||||
if (imageTask == null || imageTask.getStatus() != RUNNING) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
// store images directly
|
||||
File imageFile = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES), filename);
|
||||
OutputStream fileStream = new FileOutputStream(imageFile);
|
||||
imageTask = new ImageSaver(this);
|
||||
imageTask.execute(image, fileStream);
|
||||
} else {
|
||||
// use scoped storage
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
|
||||
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH, DIRECTORY_PICTURES);
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri imageUri = getContentResolver().insert(EXTERNAL_CONTENT_URI, values);
|
||||
if (imageUri != null) {
|
||||
OutputStream fileStream = getContentResolver().openOutputStream(imageUri);
|
||||
imageTask = new ImageSaver(this);
|
||||
imageTask.execute(image, fileStream);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// store images directly
|
||||
File imageFile = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES), filename + ".jpg");
|
||||
OutputStream fileStream = new FileOutputStream(imageFile);
|
||||
imageSaved = image.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, fileStream);
|
||||
fileStream.close();
|
||||
// start media scanner to scan for new image
|
||||
String[] fileName = {imageFile.toString()};
|
||||
MediaScannerConnection.scanFile(getApplicationContext(), fileName, null, null);
|
||||
}
|
||||
} catch (Exception err) {
|
||||
} catch (FileNotFoundException err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
if (imageSaved) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getApplicationContext(), R.string.info_image_saved, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(getApplicationContext(), R.string.error_image_save, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -235,6 +221,25 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
public final void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
}
|
||||
|
||||
/**
|
||||
* called when an image was successfully saved to external storage
|
||||
*/
|
||||
public void onImageSaved() {
|
||||
Toast.makeText(getApplicationContext(), R.string.info_image_saved, Toast.LENGTH_LONG).show();
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
// start media scanner to scan for new image
|
||||
String[] fileName = {filename};
|
||||
MediaScannerConnection.scanFile(getApplicationContext(), fileName, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called when an error occurs while storing image
|
||||
*/
|
||||
public void onError() {
|
||||
Toast.makeText(getApplicationContext(), R.string.error_image_save, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for GPS location
|
||||
*/
|
||||
@ -268,9 +273,12 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
protected void storeImage(Bitmap image, String filename) {
|
||||
this.image = image;
|
||||
this.filename = filename;
|
||||
if (!filename.endsWith(".jpg")) {
|
||||
this.filename += ".jpg";
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& checkSelfPermission(PERMISSIONS[2][0]) == PERMISSION_GRANTED) {
|
||||
writeImageToStorage();
|
||||
|| checkSelfPermission(PERMISSIONS[2][0]) == PERMISSION_GRANTED) {
|
||||
saveImage();
|
||||
} else {
|
||||
requestPermissions(PERMISSIONS[2], REQUEST_STORE_IMG);
|
||||
}
|
||||
@ -290,6 +298,7 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
*/
|
||||
@SuppressLint("MissingPermission") // suppressing because of an android studio bug
|
||||
private void fetchLocation() {
|
||||
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null && locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, null);
|
||||
locationPending = true;
|
||||
@ -331,16 +340,6 @@ abstract class MediaActivity extends AppCompatActivity implements LocationListen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* start background thread to save image
|
||||
*/
|
||||
private void writeImageToStorage() {
|
||||
if (imageSaveThread == null || !imageSaveThread.isAlive()) {
|
||||
imageSaveThread = new Thread(this);
|
||||
imageSaveThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called when location information was successfully fetched
|
||||
*
|
||||
|
@ -40,6 +40,7 @@ import org.nuclearfog.twidda.backend.utils.StringTools;
|
||||
import org.nuclearfog.twidda.database.GlobalSettings;
|
||||
import org.nuclearfog.zoomview.ZoomView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -116,6 +117,7 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
||||
IDLE
|
||||
}
|
||||
|
||||
private WeakReference<MediaViewer> updateEvent = new WeakReference<>(this);
|
||||
@Nullable
|
||||
private ScheduledExecutorService progressUpdate;
|
||||
@Nullable
|
||||
@ -186,14 +188,19 @@ public class MediaViewer extends MediaActivity implements OnImageClickListener,
|
||||
controlPanel.setVisibility(VISIBLE);
|
||||
if (!mediaLinks[0].startsWith("http"))
|
||||
share.setVisibility(GONE); // local image
|
||||
final Runnable seekUpdate = new Runnable() {
|
||||
public void run() {
|
||||
if (updateEvent.get() != null) {
|
||||
updateEvent.get().updateSeekBar();
|
||||
}
|
||||
}
|
||||
};
|
||||
progressUpdate = Executors.newScheduledThreadPool(1);
|
||||
progressUpdate.scheduleWithFixedDelay(new Runnable() {
|
||||
public void run() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
updateSeekBar();
|
||||
}
|
||||
});
|
||||
if (updateEvent.get() != null) {
|
||||
updateEvent.get().runOnUiThread(seekUpdate);
|
||||
}
|
||||
}
|
||||
}, PROGRESS_UPDATE, PROGRESS_UPDATE, TimeUnit.MILLISECONDS);
|
||||
case MEDIAVIEWER_ANGIF:
|
||||
|
@ -4,6 +4,7 @@ import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
@ -23,9 +24,11 @@ public final class ImageItem extends RecyclerView.ViewHolder {
|
||||
|
||||
public ImageItem(View view, GlobalSettings settings) {
|
||||
super(view);
|
||||
CardView cardBackground = (CardView) view;
|
||||
preview = view.findViewById(R.id.item_image_preview);
|
||||
saveButton = view.findViewById(R.id.item_image_save);
|
||||
saveButton.setImageResource(R.drawable.save);
|
||||
cardBackground.setCardBackgroundColor(settings.getCardColor());
|
||||
AppStyles.setButtonColor(saveButton, settings.getFontColor());
|
||||
AppStyles.setDrawableColor(saveButton, settings.getIconColor());
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package org.nuclearfog.twidda.backend;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import org.nuclearfog.twidda.activity.MediaActivity;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* background task to save an image to the external storage
|
||||
*
|
||||
* @author nuclearfog
|
||||
* @see MediaActivity
|
||||
*/
|
||||
public class ImageSaver extends AsyncTask<Object, Void, Boolean> {
|
||||
|
||||
/**
|
||||
* Quality of the saved jpeg images
|
||||
*/
|
||||
private static final int JPEG_QUALITY = 90;
|
||||
|
||||
private WeakReference<MediaActivity> callback;
|
||||
|
||||
|
||||
public ImageSaver(MediaActivity activity) {
|
||||
super();
|
||||
callback = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Object... data) {
|
||||
try {
|
||||
if (data != null && data.length == 2) {
|
||||
if (data[0] instanceof Bitmap && data[1] instanceof OutputStream) {
|
||||
Bitmap image = (Bitmap) data[0];
|
||||
OutputStream fileStream = (OutputStream) data[1];
|
||||
boolean imageSaved = image.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, fileStream);
|
||||
fileStream.close();
|
||||
return imageSaved;
|
||||
}
|
||||
}
|
||||
} catch (Exception err) {
|
||||
err.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean success) {
|
||||
if (callback.get() != null) {
|
||||
if (success) {
|
||||
callback.get().onImageSaved();
|
||||
} else {
|
||||
callback.get().onError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user