added media metadata support, bug fix
This commit is contained in:
parent
5808274a1e
commit
1b9edd2a9e
@ -44,11 +44,13 @@ public class MastodonMedia implements Media {
|
||||
private String description = "";
|
||||
private String blur;
|
||||
private int type = UNDEFINED;
|
||||
private Meta meta;
|
||||
|
||||
/**
|
||||
* @param json Mastodon status JSON format
|
||||
*/
|
||||
public MastodonMedia(JSONObject json) throws JSONException {
|
||||
JSONObject metaJson = json.optJSONObject("meta");
|
||||
String typeStr = json.getString("type");
|
||||
String url = json.getString("url");
|
||||
String preview = json.optString("preview_url", "");
|
||||
@ -83,6 +85,9 @@ public class MastodonMedia implements Media {
|
||||
if (json.has("description") && !json.isNull("description")) {
|
||||
description = json.getString("description");
|
||||
}
|
||||
if (metaJson != null) {
|
||||
meta = new MastodonMeta(metaJson);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -122,6 +127,13 @@ public class MastodonMedia implements Media {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Meta getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Media))
|
||||
@ -154,4 +166,90 @@ public class MastodonMedia implements Media {
|
||||
}
|
||||
return tostring + " url=\"" + getUrl() + "\"";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final class MastodonMeta implements Meta {
|
||||
|
||||
private static final long serialVersionUID = 5103849502754551661L;
|
||||
|
||||
private double duration;
|
||||
private int previewWidth;
|
||||
private int previewHeight;
|
||||
private int originalWidth;
|
||||
private int originalHeight;
|
||||
private int bitrate;
|
||||
private float framerate;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MastodonMeta(JSONObject json) {
|
||||
JSONObject original = json.optJSONObject("original");
|
||||
JSONObject small = json.optJSONObject("small");
|
||||
if (small != null) {
|
||||
previewWidth = small.optInt("width", 1);
|
||||
previewHeight = small.optInt("height", 1);
|
||||
}
|
||||
if (original != null) {
|
||||
String framerateStr = original.optString("frame_rate");
|
||||
originalWidth = original.optInt("width", 1);
|
||||
originalHeight = original.optInt("height", 1);
|
||||
bitrate = original.optInt("bitrate", 0) / 1024;
|
||||
duration = original.optDouble("duration", 0.0);
|
||||
// calculate framerate if any
|
||||
int split = framerateStr.indexOf("/");
|
||||
if (split > 0) {
|
||||
String upper = framerateStr.substring(0, split);
|
||||
String down = framerateStr.substring(split + 1);
|
||||
if (upper.matches("\\d+") && down.matches("\\d+") && !down.equals("0")) {
|
||||
framerate = (float) (Integer.parseInt(upper) / Integer.parseInt(down));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getWidthPreview() {
|
||||
return previewWidth;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getHeightPreview() {
|
||||
return previewHeight;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return originalWidth;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return originalHeight;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getBitrate() {
|
||||
return bitrate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getFrameRate() {
|
||||
return framerate;
|
||||
}
|
||||
}
|
||||
}
|
@ -139,6 +139,13 @@ public class MediaV1 implements Media {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Meta getMeta() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Media))
|
||||
|
@ -145,6 +145,13 @@ public class MediaV2 implements Media {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Meta getMeta() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Media))
|
||||
|
@ -9,7 +9,6 @@ import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.nuclearfog.twidda.BuildConfig;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@ -64,7 +63,7 @@ public abstract class AsyncExecutor<Parameter, Result> {
|
||||
try {
|
||||
Result result = doInBackground(parameter);
|
||||
onPostExecute(result, callbackReference);
|
||||
} catch (InterruptedException | InterruptedIOException e) {
|
||||
} catch (InterruptedException e) {
|
||||
// caused by user interaction. No need to handle exception.
|
||||
} catch (Exception e) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
@ -121,7 +120,7 @@ public abstract class AsyncExecutor<Parameter, Result> {
|
||||
* @return result of the background task
|
||||
*/
|
||||
@WorkerThread
|
||||
protected abstract Result doInBackground(@NonNull Parameter param) throws InterruptedException, InterruptedIOException;
|
||||
protected abstract Result doInBackground(@NonNull Parameter param) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Callback used to send task result to main thread
|
||||
|
@ -11,6 +11,15 @@ import android.util.SparseArray;
|
||||
*/
|
||||
public class BlurHashDecoder {
|
||||
|
||||
/**
|
||||
* use in memory cache for the calculated math, reused by images with same size.
|
||||
* if the cache does not exist yet it will be created and populated with new calculations.
|
||||
* By default it is true.
|
||||
*/
|
||||
private static final boolean USE_CACHE = true;
|
||||
|
||||
private static final int DEFAULT_SIZE = 16;
|
||||
|
||||
// cache Math.cos() calculations to improve performance.
|
||||
// The number of calculations can be huge for many bitmaps: width * height * numCompX * numCompY * 2 * nBitmaps
|
||||
// the cache is enabled by default, it is recommended to disable it only when just a few images are displayed
|
||||
@ -36,20 +45,40 @@ public class BlurHashDecoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* create blurred bitmap using hash string
|
||||
*
|
||||
* @param blurHash hash string
|
||||
* @return blurred bitmap
|
||||
*/
|
||||
public static Bitmap decode(String blurHash) {
|
||||
return decode(blurHash, 16, 16, 1f, true);
|
||||
return decode(blurHash, DEFAULT_SIZE, DEFAULT_SIZE, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a blur hash into a new bitmap.
|
||||
* create scaled bitmap using hash string
|
||||
*
|
||||
* @param useCache use in memory cache for the calculated math, reused by images with same size.
|
||||
* if the cache does not exist yet it will be created and populated with new calculations.
|
||||
* By default it is true.
|
||||
* @param blurHash hash string
|
||||
* @param ratio ratio ob the bitmap to generate
|
||||
* @return blurred bitmap
|
||||
*/
|
||||
public static Bitmap decode(String blurHash, int width, int height, float punch, boolean useCache) {
|
||||
public static Bitmap decode(String blurHash, float ratio) {
|
||||
if (ratio > 1.0f) {
|
||||
return decode(blurHash, DEFAULT_SIZE, (int) (DEFAULT_SIZE / ratio), 1f);
|
||||
} else if (ratio < 1.0f && ratio > 0.0f) {
|
||||
return decode(blurHash, (int) (DEFAULT_SIZE * ratio), DEFAULT_SIZE, 1f);
|
||||
}
|
||||
return decode(blurHash, DEFAULT_SIZE, DEFAULT_SIZE, 1f);
|
||||
}
|
||||
|
||||
/**
|
||||
* create blurred bitmap with custom size
|
||||
*
|
||||
* @param blurHash hash string
|
||||
* @param width bitmap width
|
||||
* @param height bitmap height
|
||||
* @return blurred bitmap
|
||||
*/
|
||||
public static Bitmap decode(String blurHash, int width, int height, float punch) {
|
||||
if (blurHash == null || blurHash.length() < 6)
|
||||
return null;
|
||||
int numCompEnc = decode83(blurHash, 0, 1);
|
||||
@ -66,7 +95,7 @@ public class BlurHashDecoder {
|
||||
int colorEnc = decode83(blurHash, from, from + 2);
|
||||
colors[i] = decodeAc(colorEnc, maxAc * punch);
|
||||
}
|
||||
return composeBitmap(width, height, numCompX, numCompY, colors, useCache);
|
||||
return composeBitmap(width, height, numCompX, numCompY, colors);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,12 +149,12 @@ public class BlurHashDecoder {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static Bitmap composeBitmap(int width, int height, int numCompX, int numCompY, float[][] colors, boolean useCache) {
|
||||
private static Bitmap composeBitmap(int width, int height, int numCompX, int numCompY, float[][] colors) {
|
||||
// use an array for better performance when writing pixel colors
|
||||
int[] imageArray = new int[width * height];
|
||||
boolean calculateCosX = !useCache || cacheCosinesX.get(width * numCompX) == null;
|
||||
boolean calculateCosX = !USE_CACHE || cacheCosinesX.get(width * numCompX) == null;
|
||||
double[] cosinesX = getArrayForCosinesX(calculateCosX, width, numCompX);
|
||||
boolean calculateCosY = !useCache || cacheCosinesY.get(height * numCompY) == null;
|
||||
boolean calculateCosY = !USE_CACHE || cacheCosinesY.get(height * numCompY) == null;
|
||||
double[] cosinesY = getArrayForCosinesY(calculateCosY, height, numCompY);
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
@ -89,6 +89,13 @@ public class DatabaseMedia implements Media, MediaTable {
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Meta getMeta() {
|
||||
return null; // todo implement this
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Media))
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.nuclearfog.twidda.model;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
@ -64,9 +66,62 @@ public interface Media extends Serializable, Comparable<Media> {
|
||||
*/
|
||||
String getBlurHash();
|
||||
|
||||
/**
|
||||
* @return media information
|
||||
*/
|
||||
@Nullable
|
||||
Meta getMeta();
|
||||
|
||||
|
||||
@Override
|
||||
default int compareTo(Media o) {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(getKey(), o.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Media information
|
||||
*/
|
||||
interface Meta extends Serializable {
|
||||
|
||||
/**
|
||||
* get duration if video
|
||||
*
|
||||
* @return video duration in seconds
|
||||
*/
|
||||
double getDuration();
|
||||
|
||||
/**
|
||||
* @return image width of the thumbnail
|
||||
*/
|
||||
int getWidthPreview();
|
||||
|
||||
/**
|
||||
* @return image height of the thumbnail
|
||||
*/
|
||||
int getHeightPreview();
|
||||
|
||||
/**
|
||||
* @return image/video with
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* @return image/video height
|
||||
*/
|
||||
int getHeight();
|
||||
|
||||
/**
|
||||
* get audio/video if any
|
||||
*
|
||||
* @return bitrate in kbit/s
|
||||
*/
|
||||
int getBitrate();
|
||||
|
||||
/**
|
||||
* get video framerate if any
|
||||
*
|
||||
* @return frame rate
|
||||
*/
|
||||
float getFrameRate();
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Media;
|
||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
||||
import org.nuclearfog.twidda.ui.dialogs.MetaDialog;
|
||||
import org.nuclearfog.twidda.ui.views.AnimatedImageView;
|
||||
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
||||
import org.nuclearfog.zoomview.ZoomView;
|
||||
@ -65,6 +66,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
private DescriptionView descriptionView;
|
||||
|
||||
private DescriptionDialog descriptionDialog;
|
||||
private MetaDialog metaDialog;
|
||||
|
||||
@Nullable
|
||||
private Uri cacheUri;
|
||||
@ -74,6 +76,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
private ImageDownloader imageAsync;
|
||||
private GlobalSettings settings;
|
||||
private File cacheFolder;
|
||||
private Media.Meta meta;
|
||||
|
||||
|
||||
@Override
|
||||
@ -100,6 +103,8 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
|
||||
imageAsync = new ImageDownloader(this);
|
||||
descriptionDialog = new DescriptionDialog(this, this);
|
||||
metaDialog = new MetaDialog(this);
|
||||
|
||||
cacheFolder = new File(getExternalCacheDir(), ImageViewer.CACHE_FOLDER);
|
||||
cacheFolder.mkdirs();
|
||||
|
||||
@ -109,6 +114,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
String description = null;
|
||||
boolean animated = false;
|
||||
boolean local = false;
|
||||
float ratio = 1.0f;
|
||||
Serializable serializedData;
|
||||
if (savedInstanceState != null) {
|
||||
serializedData = savedInstanceState.getSerializable(KEY_IMAGE_DATA);
|
||||
@ -123,10 +129,14 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
description = mediaStatus.getDescription();
|
||||
} else if (serializedData instanceof Media) {
|
||||
Media media = (Media) serializedData;
|
||||
meta = media .getMeta();
|
||||
blurHash = media.getBlurHash();
|
||||
imageUrl = media.getUrl();
|
||||
description = media.getDescription();
|
||||
animated = media.getMediaType() == Media.GIF;
|
||||
if (meta != null) {
|
||||
ratio = meta.getWidth() / (float) meta.getHeight();
|
||||
}
|
||||
} else if (serializedData instanceof String) {
|
||||
imageUrl = (String) serializedData;
|
||||
} else {
|
||||
@ -161,7 +171,7 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
}
|
||||
// set image blur placeholder
|
||||
if (blurHash != null && !blurHash.trim().isEmpty()) {
|
||||
Bitmap blur = BlurHashDecoder.decode(blurHash);
|
||||
Bitmap blur = BlurHashDecoder.decode(blurHash, ratio);
|
||||
zoomImage.setImageBitmap(blur);
|
||||
zoomImage.setMovable(false);
|
||||
}
|
||||
@ -201,7 +211,9 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuItem itemSave = menu.findItem(R.id.menu_image_save);
|
||||
MenuItem itemMeta = menu.findItem(R.id.menu_image_show_meta);
|
||||
itemSave.setVisible(cacheUri != null);
|
||||
itemMeta.setVisible(meta != null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -216,6 +228,11 @@ public class ImageViewer extends MediaActivity implements AsyncCallback<ImageLoa
|
||||
} else if (item.getItemId() == R.id.menu_image_add_description) {
|
||||
descriptionDialog.show();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_image_show_meta) {
|
||||
if (meta != null) {
|
||||
metaDialog.show(meta);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||
import org.nuclearfog.twidda.config.Configuration;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConnectionDialog;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -51,7 +51,7 @@ import org.nuclearfog.twidda.config.Configuration;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Location;
|
||||
import org.nuclearfog.twidda.notification.PushSubscription;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||
import org.nuclearfog.twidda.ui.dialogs.InfoDialog;
|
||||
|
@ -46,6 +46,7 @@ import org.nuclearfog.twidda.backend.utils.LinkUtils;
|
||||
import org.nuclearfog.twidda.model.Media;
|
||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog;
|
||||
import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback;
|
||||
import org.nuclearfog.twidda.ui.dialogs.MetaDialog;
|
||||
import org.nuclearfog.twidda.ui.views.DescriptionView;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -83,6 +84,7 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
||||
private PlayerView playerView;
|
||||
|
||||
private DescriptionDialog descriptionDialog;
|
||||
private MetaDialog metaDialog;
|
||||
|
||||
private ExoPlayer player;
|
||||
@Nullable
|
||||
@ -105,6 +107,7 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
||||
toolbar = findViewById(R.id.page_video_toolbar);
|
||||
descriptionView = findViewById(R.id.page_video_description);
|
||||
descriptionDialog = new DescriptionDialog(this, this);
|
||||
metaDialog = new MetaDialog(this);
|
||||
player = new ExoPlayer.Builder(this, this).build();
|
||||
|
||||
toolbar.setTitle("");
|
||||
@ -233,9 +236,11 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
||||
getMenuInflater().inflate(R.menu.video, menu);
|
||||
MenuItem menuOpenUrl = menu.findItem(R.id.menu_video_link);
|
||||
MenuItem menuDescription = menu.findItem(R.id.menu_video_add_description);
|
||||
MenuItem menuMeta = menu.findItem(R.id.menu_video_show_meta);
|
||||
AppStyles.setMenuIconColor(menu, Color.WHITE);
|
||||
menuOpenUrl.setVisible(media != null);
|
||||
menuDescription.setVisible(mediaStatus != null);
|
||||
menuMeta.setVisible(media != null && media.getMeta() != null);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@ -246,9 +251,11 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener, D
|
||||
if (media != null) {
|
||||
LinkUtils.openMediaLink(this, Uri.parse(media.getUrl()));
|
||||
}
|
||||
}
|
||||
//
|
||||
else if (item.getItemId() == R.id.menu_video_add_description) {
|
||||
} else if (item.getItemId() == R.id.menu_video_show_meta) {
|
||||
if (media != null && media.getMeta() != null) {
|
||||
metaDialog.show(media.getMeta());
|
||||
}
|
||||
} else if (item.getItemId() == R.id.menu_video_add_description) {
|
||||
descriptionDialog.show();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
@ -1,4 +1,4 @@
|
||||
package org.nuclearfog.twidda.ui.adapter.recyclerview;
|
||||
package org.nuclearfog.twidda.ui.adapter.listview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
@ -60,7 +60,7 @@ public class DropdownAdapter extends BaseAdapter {
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView textItem;
|
||||
if (convertView == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
convertView = inflater.inflate(R.layout.item_dropdown, parent, false);
|
||||
textItem = convertView.findViewById(R.id.dropdown_textitem);
|
||||
textItem.setTextColor(settings.getTextColor());
|
@ -0,0 +1,119 @@
|
||||
package org.nuclearfog.twidda.ui.adapter.listview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Media;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ListView adapter used to show Meta items
|
||||
*
|
||||
* @see org.nuclearfog.twidda.ui.dialogs.MetaDialog
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class MetaAdapter extends BaseAdapter {
|
||||
|
||||
private List<String> keys = new ArrayList<>();
|
||||
private List<String> values = new ArrayList<>();
|
||||
|
||||
private Context context;
|
||||
private GlobalSettings settings;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MetaAdapter(Context context) {
|
||||
this.context = context;
|
||||
settings = GlobalSettings.get(context);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return Math.min(keys.size(), values.size());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return new String[] {keys.get(position), values.get(position)};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return keys.get(position).hashCode();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView key_text;
|
||||
TextView value_text;
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_meta_field, parent, false);
|
||||
key_text = convertView.findViewById(R.id.item_meta_field_key);
|
||||
value_text = convertView.findViewById(R.id.item_meta_field_value);
|
||||
key_text.setTextColor(settings.getTextColor());
|
||||
value_text.setTextColor(settings.getTextColor());
|
||||
} else {
|
||||
key_text = convertView.findViewById(R.id.item_meta_field_key);
|
||||
value_text = convertView.findViewById(R.id.item_meta_field_value);
|
||||
}
|
||||
key_text.setText(keys.get(position));
|
||||
value_text.setText(values.get(position));
|
||||
return convertView;
|
||||
}
|
||||
|
||||
/**
|
||||
* set adapter items
|
||||
*
|
||||
* @param meta media meta information
|
||||
*/
|
||||
public void setItems(Media.Meta meta) {
|
||||
if (meta.getWidth() > 1 && meta.getHeight() > 1) {
|
||||
keys.add(context.getString(R.string.dialog_meta_width));
|
||||
values.add(StringUtils.NUMBER_FORMAT.format(meta.getWidth()));
|
||||
keys.add(context.getString(R.string.dialog_meta_height));
|
||||
values.add(StringUtils.NUMBER_FORMAT.format(meta.getHeight()));
|
||||
}
|
||||
if (meta.getBitrate() > 0) {
|
||||
keys.add(context.getString(R.string.dialog_meta_bitrate));
|
||||
values.add(StringUtils.NUMBER_FORMAT.format(meta.getBitrate()) + " kbit/s");
|
||||
}
|
||||
if (meta.getFrameRate() > 0) {
|
||||
keys.add(context.getString(R.string.dialog_meta_framerate));
|
||||
values.add(StringUtils.NUMBER_FORMAT.format(meta.getFrameRate()) + " fps");
|
||||
}
|
||||
if (meta.getDuration() > 0) {
|
||||
StringBuilder durationValue = new StringBuilder();
|
||||
keys.add(context.getString(R.string.dialog_meta_duration));
|
||||
long hours = Math.round(meta.getDuration() / 3600);
|
||||
long mins = Math.round(meta.getDuration() / 60) % 60L;
|
||||
long sec = Math.round(meta.getDuration() % 60.0);
|
||||
if (hours > 0) {
|
||||
if (hours < 10)
|
||||
durationValue.append('0');
|
||||
durationValue.append(hours).append(':');
|
||||
}
|
||||
if (mins < 10)
|
||||
durationValue.append('0');
|
||||
durationValue.append(mins).append(':');
|
||||
if (sec < 10)
|
||||
durationValue.append('0');
|
||||
durationValue.append(sec).append('\n');
|
||||
values.add(durationValue.toString());
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ import org.nuclearfog.twidda.backend.helper.update.FilterUpdate;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||
import org.nuclearfog.twidda.model.Filter;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
package org.nuclearfog.twidda.ui.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.model.Media.Meta;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.MetaAdapter;
|
||||
|
||||
/**
|
||||
* Dialog to show media information
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class MetaDialog extends Dialog {
|
||||
|
||||
private MetaAdapter adapter;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public MetaDialog(Activity activity) {
|
||||
super(activity, R.style.MetaDialog);
|
||||
adapter = new MetaAdapter(activity.getApplicationContext());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_meta);
|
||||
ViewGroup root = findViewById(R.id.dialog_meta_root);
|
||||
ListView list = findViewById(R.id.dialog_meta_list);
|
||||
|
||||
list.setAdapter(adapter);
|
||||
AppStyles.setTheme(root);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
// using show(Meta) instead
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
if (isShowing()) {
|
||||
super.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void show(@NonNull Meta meta) {
|
||||
if (!isShowing()) {
|
||||
super.show();
|
||||
adapter.setItems(meta);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.helper.update.PollUpdate;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.model.Instance;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.EditOptionsAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -22,7 +22,7 @@ import org.nuclearfog.twidda.backend.async.ReportUpdater.ReportResult;
|
||||
import org.nuclearfog.twidda.backend.helper.update.ReportUpdate;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
@ -18,7 +18,7 @@ import org.nuclearfog.twidda.backend.helper.update.StatusUpdate;
|
||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Status;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -27,7 +27,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.WebPush;
|
||||
import org.nuclearfog.twidda.ui.adapter.recyclerview.DropdownAdapter;
|
||||
import org.nuclearfog.twidda.ui.adapter.listview.DropdownAdapter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
25
app/src/main/res/layout/dialog_meta.xml
Normal file
25
app/src/main/res/layout/dialog_meta.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/dialog_meta_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.dialogs.MetaDialog">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialog_meta_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:text="@string/dialog_meta_title"
|
||||
android:textSize="@dimen/dialog_meta_textsize_title"
|
||||
android:layout_margin="@dimen/dialog_meta_margin_layout" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/dialog_meta_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/dialog_meta_margin_layout" />
|
||||
|
||||
</LinearLayout>
|
23
app/src/main/res/layout/item_meta_field.xml
Normal file
23
app/src/main/res/layout/item_meta_field.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_meta_field_key"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2"
|
||||
android:layout_marginEnd="@dimen/dialog_media_field_layout_margin"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_meta_field_value"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="top|end"
|
||||
android:maxLines="2" />
|
||||
|
||||
</LinearLayout>
|
@ -3,9 +3,13 @@
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_image_save"
|
||||
android:icon="@drawable/save"
|
||||
android:title="@string/item_image_save" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_image_show_meta"
|
||||
android:title="@string/menu_media_show_meta"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_image_add_description"
|
||||
android:title="@string/menu_media_add_description"
|
||||
|
@ -6,6 +6,11 @@
|
||||
android:title="@string/button_share"
|
||||
android:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_video_show_meta"
|
||||
android:title="@string/menu_media_show_meta"
|
||||
android:visible="false" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_video_add_description"
|
||||
android:title="@string/menu_media_add_description"
|
||||
|
@ -268,6 +268,7 @@
|
||||
<string name="popup_status_text_hint">Status</string>
|
||||
<string name="info_account_selected">%1$s ausgewählt</string>
|
||||
<string name="menu_media_add_description">Beschreibung hinzufügen</string>
|
||||
<string name="menu_media_show_meta">zeige Medieninformationen</string>
|
||||
<string name="menu_message">Nachricht schreiben</string>
|
||||
<string name="status_replyname_empty">Antwort</string>
|
||||
<string name="status_metrics_title">Statusmetriken</string>
|
||||
@ -334,6 +335,12 @@
|
||||
<string name="dialog_report_category_label">Meldegrund</string>
|
||||
<string name="dialog_report_title_status">Status melden</string>
|
||||
<string name="dialog_report_apply">melden</string>
|
||||
<string name="dialog_meta_title">Medieninformationen</string>
|
||||
<string name="dialog_meta_width">Breite</string>
|
||||
<string name="dialog_meta_height">Höhe</string>
|
||||
<string name="dialog_meta_bitrate">Bitrate</string>
|
||||
<string name="dialog_meta_framerate">Framerate</string>
|
||||
<string name="dialog_meta_duration">Dauer</string>
|
||||
<string name="settings_enable_push_label">aktiviere Push-Benachrichtigung\n(ntfy-App erforderlich)</string>
|
||||
<string name="notification_favorite">%1$s hat einen Status favorisiert</string>
|
||||
<string name="notification_repost">%1$s hat einen Status geteilt</string>
|
||||
|
@ -373,4 +373,11 @@
|
||||
<dimen name="item_field_textsize_time">10sp</dimen>
|
||||
<dimen name="item_field_icon_size">18sp</dimen>
|
||||
|
||||
<!--dimens of dialog_meta.xml-->
|
||||
<dimen name="dialog_meta_textsize_title">22sp</dimen>
|
||||
<dimen name="dialog_meta_margin_layout">5dp</dimen>
|
||||
|
||||
<!--dimens of item_media_field.xml-->
|
||||
<dimen name="dialog_media_field_layout_margin">5dp</dimen>
|
||||
|
||||
</resources>
|
@ -192,6 +192,7 @@
|
||||
<string name="menu_toolbar_request">Follow requests</string>
|
||||
<string name="menu_toolbar_excluded_users">Blocklists</string>
|
||||
<string name="menu_media_add_description">add description</string>
|
||||
<string name="menu_media_show_meta">show metadata</string>
|
||||
<string name="menu_message">write message</string>
|
||||
<string name="dialog_link_image_preview">Link preview image</string>
|
||||
<string name="app_info_icons">svg icons from:</string>
|
||||
@ -358,6 +359,12 @@
|
||||
<string name="dialog_report_category_label">what to report</string>
|
||||
<string name="dialog_report_title_status">report status</string>
|
||||
<string name="dialog_report_apply">report</string>
|
||||
<string name="dialog_meta_title">Media information</string>
|
||||
<string name="dialog_meta_width">width</string>
|
||||
<string name="dialog_meta_height">height</string>
|
||||
<string name="dialog_meta_bitrate">bitrate</string>
|
||||
<string name="dialog_meta_framerate">framerate</string>
|
||||
<string name="dialog_meta_duration">duration</string>
|
||||
<string name="settings_enable_push_label">enable push notification\n(requires ntfy app)</string>
|
||||
<string name="notification_favorite">%1$s favorited your status</string>
|
||||
<string name="notification_repost">%1$s reposted your status</string>
|
||||
|
@ -23,6 +23,10 @@
|
||||
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||
</style>
|
||||
|
||||
<style name="MetaDialog" parent="DefaultDialog">
|
||||
<item name="android:windowMinWidthMinor">60%</item>
|
||||
</style>
|
||||
|
||||
<style name="AppInfoDialog" parent="DefaultDialog">
|
||||
<item name="android:background">@android:color/white</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user