Merge branch 'master' into fork

This commit is contained in:
sk 2022-06-08 21:42:42 +02:00
commit e0e48f87eb
3 changed files with 148 additions and 40 deletions

View File

@ -0,0 +1,39 @@
package org.joinmastodon.android.api;
import android.graphics.Rect;
import android.net.Uri;
import java.io.IOException;
public class AvatarResizedImageRequestBody extends ResizedImageRequestBody{
public AvatarResizedImageRequestBody(Uri uri, ProgressListener progressListener) throws IOException{
super(uri, 0, progressListener);
}
@Override
protected int[] getTargetSize(int srcWidth, int srcHeight){
float factor=400f/Math.min(srcWidth, srcHeight);
return new int[]{Math.round(srcWidth*factor), Math.round(srcHeight*factor)};
}
@Override
protected boolean needResize(int srcWidth, int srcHeight){
return srcHeight>400 || srcWidth!=srcHeight;
}
@Override
protected boolean needCrop(int srcWidth, int srcHeight){
return srcWidth!=srcHeight;
}
@Override
protected Rect getCropBounds(int srcWidth, int srcHeight){
Rect rect=new Rect();
if(srcWidth>srcHeight){
rect.set(srcWidth/2-srcHeight/2, 0, srcWidth/2-srcHeight/2+srcHeight, srcHeight);
}else{
rect.set(0, srcHeight/2-srcWidth/2, srcWidth, srcHeight/2-srcWidth/2+srcWidth);
}
return rect;
}
}

View File

@ -14,6 +14,7 @@ import android.os.Build;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -30,62 +31,105 @@ public class ResizedImageRequestBody extends CountingRequestBody{
private File tempFile; private File tempFile;
private Uri uri; private Uri uri;
private String contentType; private String contentType;
private int maxSize;
public ResizedImageRequestBody(Uri uri, int maxSize, ProgressListener progressListener) throws IOException{ public ResizedImageRequestBody(Uri uri, int maxSize, ProgressListener progressListener) throws IOException{
super(progressListener); super(progressListener);
this.uri=uri; this.uri=uri;
contentType=MastodonApp.context.getContentResolver().getType(uri); this.maxSize=maxSize;
BitmapFactory.Options opts=new BitmapFactory.Options(); BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inJustDecodeBounds=true; opts.inJustDecodeBounds=true;
try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){ if("file".equals(uri.getScheme())){
BitmapFactory.decodeStream(in, null, opts); BitmapFactory.decodeFile(uri.getPath(), opts);
contentType=UiUtils.getFileMediaType(new File(uri.getPath())).type();
}else{
try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){
BitmapFactory.decodeStream(in, null, opts);
}
contentType=MastodonApp.context.getContentResolver().getType(uri);
} }
if(opts.outWidth*opts.outHeight>maxSize){ if(needResize(opts.outWidth, opts.outHeight) || needCrop(opts.outWidth, opts.outHeight)){
Bitmap bitmap; Bitmap bitmap;
if(Build.VERSION.SDK_INT>=29){ if(Build.VERSION.SDK_INT>=28){
bitmap=ImageDecoder.decodeBitmap(ImageDecoder.createSource(MastodonApp.context.getContentResolver(), uri), (decoder, info, source)->{ ImageDecoder.Source source;
int targetWidth=Math.round((float)Math.sqrt((float)maxSize*((float)info.getSize().getWidth()/info.getSize().getHeight()))); if("file".equals(uri.getScheme())){
int targetHeight=Math.round((float)Math.sqrt((float)maxSize*((float)info.getSize().getHeight()/info.getSize().getWidth()))); source=ImageDecoder.createSource(new File(uri.getPath()));
}else{
source=ImageDecoder.createSource(MastodonApp.context.getContentResolver(), uri);
}
bitmap=ImageDecoder.decodeBitmap(source, (decoder, info, _source)->{
int[] size=getTargetSize(info.getSize().getWidth(), info.getSize().getHeight());
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
decoder.setTargetSize(targetWidth, targetHeight); decoder.setTargetSize(size[0], size[1]);
// Breaks images in mysterious ways
// if(needCrop(size[0], size[1]))
// decoder.setCrop(getCropBounds(size[0], size[1]));
}); });
if(needCrop(bitmap.getWidth(), bitmap.getHeight())){
Rect crop=getCropBounds(bitmap.getWidth(), bitmap.getHeight());
bitmap=Bitmap.createBitmap(bitmap, crop.left, crop.top, crop.width(), crop.height());
}
}else{ }else{
int targetWidth=Math.round((float)Math.sqrt((float)maxSize*((float)opts.outWidth/opts.outHeight))); int[] size=getTargetSize(opts.outWidth, opts.outHeight);
int targetHeight=Math.round((float)Math.sqrt((float)maxSize*((float)opts.outHeight/opts.outWidth))); int targetWidth=size[0];
int targetHeight=size[1];
float factor=opts.outWidth/(float)targetWidth; float factor=opts.outWidth/(float)targetWidth;
opts=new BitmapFactory.Options(); opts=new BitmapFactory.Options();
opts.inSampleSize=(int)factor; opts.inSampleSize=(int)factor;
try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){ if("file".equals(uri.getScheme())){
bitmap=BitmapFactory.decodeStream(in, null, opts); bitmap=BitmapFactory.decodeFile(uri.getPath(), opts);
}else{
try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){
bitmap=BitmapFactory.decodeStream(in, null, opts);
}
} }
if(factor%1f!=0f){ boolean needCrop=needCrop(targetWidth, targetHeight);
Bitmap scaled=Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888); if(factor%1f!=0f || needCrop){
new Canvas(scaled).drawBitmap(bitmap, null, new Rect(0, 0, targetWidth, targetHeight), new Paint(Paint.FILTER_BITMAP_FLAG)); Rect srcBounds=null;
Rect dstBounds;
if(needCrop){
Rect crop=getCropBounds(targetWidth, targetHeight);
dstBounds=new Rect(0, 0, crop.width(), crop.height());
srcBounds=new Rect(
Math.round(crop.left/(float)targetWidth*bitmap.getWidth()),
Math.round(crop.top/(float)targetHeight*bitmap.getHeight()),
Math.round(crop.right/(float)targetWidth*bitmap.getWidth()),
Math.round(crop.bottom/(float)targetHeight*bitmap.getHeight())
);
}else{
dstBounds=new Rect(0, 0, targetWidth, targetHeight);
}
Bitmap scaled=Bitmap.createBitmap(dstBounds.width(), dstBounds.height(), Bitmap.Config.ARGB_8888);
new Canvas(scaled).drawBitmap(bitmap, srcBounds, dstBounds, new Paint(Paint.FILTER_BITMAP_FLAG));
bitmap=scaled; bitmap=scaled;
} }
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ int orientation=0;
int rotation; if("file".equals(uri.getScheme())){
ExifInterface exif=new ExifInterface(uri.getPath());
orientation=exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
}else if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){ try(InputStream in=MastodonApp.context.getContentResolver().openInputStream(uri)){
ExifInterface exif=new ExifInterface(in); ExifInterface exif=new ExifInterface(in);
int orientation=exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); orientation=exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
rotation=switch(orientation){
case ExifInterface.ORIENTATION_ROTATE_90 -> 90;
case ExifInterface.ORIENTATION_ROTATE_180 -> 180;
case ExifInterface.ORIENTATION_ROTATE_270 -> 270;
default -> 0;
};
}
if(rotation!=0){
Matrix matrix=new Matrix();
matrix.setRotate(rotation);
bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
} }
} }
int rotation=switch(orientation){
case ExifInterface.ORIENTATION_ROTATE_90 -> 90;
case ExifInterface.ORIENTATION_ROTATE_180 -> 180;
case ExifInterface.ORIENTATION_ROTATE_270 -> 270;
default -> 0;
};
if(rotation!=0){
Matrix matrix=new Matrix();
matrix.setRotate(rotation);
bitmap=Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
}
} }
tempFile=new File(MastodonApp.context.getCacheDir(), "tmp_upload_image"); boolean isPNG="image/png".equals(contentType);
tempFile=File.createTempFile("mastodon_tmp_resized", null);
try(FileOutputStream out=new FileOutputStream(tempFile)){ try(FileOutputStream out=new FileOutputStream(tempFile)){
if("image/png".equals(contentType)){ if(isPNG){
bitmap.compress(Bitmap.CompressFormat.PNG, 0, out); bitmap.compress(Bitmap.CompressFormat.PNG, 0, out);
}else{ }else{
bitmap.compress(Bitmap.CompressFormat.JPEG, 97, out); bitmap.compress(Bitmap.CompressFormat.JPEG, 97, out);
@ -94,9 +138,13 @@ public class ResizedImageRequestBody extends CountingRequestBody{
} }
length=tempFile.length(); length=tempFile.length();
}else{ }else{
try(Cursor cursor=MastodonApp.context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null)){ if("file".equals(uri.getScheme())){
cursor.moveToFirst(); length=new File(uri.getPath()).length();
length=cursor.getInt(0); }else{
try(Cursor cursor=MastodonApp.context.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null)){
cursor.moveToFirst();
length=cursor.getInt(0);
}
} }
} }
} }
@ -125,4 +173,22 @@ public class ResizedImageRequestBody extends CountingRequestBody{
} }
} }
} }
protected int[] getTargetSize(int srcWidth, int srcHeight){
int targetWidth=Math.round((float)Math.sqrt((float)maxSize*((float)srcWidth/srcHeight)));
int targetHeight=Math.round((float)Math.sqrt((float)maxSize*((float)srcHeight/srcWidth)));
return new int[]{targetWidth, targetHeight};
}
protected boolean needResize(int srcWidth, int srcHeight){
return srcWidth*srcHeight>maxSize;
}
protected boolean needCrop(int srcWidth, int srcHeight){
return false;
}
protected Rect getCropBounds(int srcWidth, int srcHeight){
return null;
}
} }

View File

@ -2,13 +2,16 @@ package org.joinmastodon.android.api.requests.accounts;
import android.net.Uri; import android.net.Uri;
import org.joinmastodon.android.api.AvatarResizedImageRequestBody;
import org.joinmastodon.android.api.ContentUriRequestBody; import org.joinmastodon.android.api.ContentUriRequestBody;
import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.ResizedImageRequestBody;
import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField; import org.joinmastodon.android.model.AccountField;
import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.utils.UiUtils;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.List; import java.util.List;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
@ -39,21 +42,21 @@ public class UpdateAccountCredentials extends MastodonAPIRequest<Account>{
} }
@Override @Override
public RequestBody getRequestBody(){ public RequestBody getRequestBody() throws IOException{
MultipartBody.Builder bldr=new MultipartBody.Builder() MultipartBody.Builder bldr=new MultipartBody.Builder()
.setType(MultipartBody.FORM) .setType(MultipartBody.FORM)
.addFormDataPart("display_name", displayName) .addFormDataPart("display_name", displayName)
.addFormDataPart("note", bio); .addFormDataPart("note", bio);
if(avatar!=null){ if(avatar!=null){
bldr.addFormDataPart("avatar", UiUtils.getFileName(avatar), new ContentUriRequestBody(avatar, null)); bldr.addFormDataPart("avatar", UiUtils.getFileName(avatar), new AvatarResizedImageRequestBody(avatar, null));
}else if(avatarFile!=null){ }else if(avatarFile!=null){
bldr.addFormDataPart("avatar", avatarFile.getName(), RequestBody.create(UiUtils.getFileMediaType(avatarFile), avatarFile)); bldr.addFormDataPart("avatar", avatarFile.getName(), new AvatarResizedImageRequestBody(Uri.fromFile(avatarFile), null));
} }
if(cover!=null){ if(cover!=null){
bldr.addFormDataPart("header", UiUtils.getFileName(cover), new ContentUriRequestBody(cover, null)); bldr.addFormDataPart("header", UiUtils.getFileName(cover), new ResizedImageRequestBody(cover, 1500*500, null));
}else if(coverFile!=null){ }else if(coverFile!=null){
bldr.addFormDataPart("header", coverFile.getName(), RequestBody.create(UiUtils.getFileMediaType(coverFile), coverFile)); bldr.addFormDataPart("header", coverFile.getName(), new ResizedImageRequestBody(Uri.fromFile(coverFile), 1500*500, null));
} }
if(fields.isEmpty()){ if(fields.isEmpty()){
bldr.addFormDataPart("fields_attributes[0][name]", "").addFormDataPart("fields_attributes[0][value]", ""); bldr.addFormDataPart("fields_attributes[0][name]", "").addFormDataPart("fields_attributes[0][value]", "");