mirror of
https://github.com/tuskyapp/Tusky
synced 2025-02-09 11:58:50 +01:00
Merge remote-tracking branch 'origin/feature_collapse_status' into feature_collapse_status
This commit is contained in:
commit
7056ba5e92
@ -101,7 +101,6 @@ import com.keylesspalace.tusky.network.ProgressRequestBody;
|
|||||||
import com.keylesspalace.tusky.service.SendTootService;
|
import com.keylesspalace.tusky.service.SendTootService;
|
||||||
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
import com.keylesspalace.tusky.util.CountUpDownLatch;
|
||||||
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
import com.keylesspalace.tusky.util.DownsizeImageTask;
|
||||||
import com.keylesspalace.tusky.util.IOUtils;
|
|
||||||
import com.keylesspalace.tusky.util.ListUtils;
|
import com.keylesspalace.tusky.util.ListUtils;
|
||||||
import com.keylesspalace.tusky.util.MediaUtils;
|
import com.keylesspalace.tusky.util.MediaUtils;
|
||||||
import com.keylesspalace.tusky.util.MentionTokenizer;
|
import com.keylesspalace.tusky.util.MentionTokenizer;
|
||||||
@ -124,7 +123,6 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -159,8 +157,9 @@ public final class ComposeActivity
|
|||||||
|
|
||||||
private static final String TAG = "ComposeActivity"; // logging tag
|
private static final String TAG = "ComposeActivity"; // logging tag
|
||||||
static final int STATUS_CHARACTER_LIMIT = 500;
|
static final int STATUS_CHARACTER_LIMIT = 500;
|
||||||
private static final int STATUS_MEDIA_SIZE_LIMIT = 8388608; // 8MiB
|
private static final int STATUS_IMAGE_SIZE_LIMIT = 8388608; // 8MiB
|
||||||
private static final int STATUS_MEDIA_PIXEL_SIZE_LIMIT = 16777216; // 4096^2 Pixels
|
private static final int STATUS_VIDEO_SIZE_LIMIT = 41943040; // 40MiB
|
||||||
|
private static final int STATUS_IMAGE_PIXEL_SIZE_LIMIT = 16777216; // 4096^2 Pixels
|
||||||
private static final int MEDIA_PICK_RESULT = 1;
|
private static final int MEDIA_PICK_RESULT = 1;
|
||||||
private static final int MEDIA_TAKE_PHOTO_RESULT = 2;
|
private static final int MEDIA_TAKE_PHOTO_RESULT = 2;
|
||||||
private static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
|
private static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
|
||||||
@ -995,8 +994,8 @@ public final class ComposeActivity
|
|||||||
@NonNull
|
@NonNull
|
||||||
private File createNewImageFile() throws IOException {
|
private File createNewImageFile() throws IOException {
|
||||||
// Create an image file name
|
// Create an image file name
|
||||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
String randomId = StringUtils.randomAlphanumericString(12);
|
||||||
String imageFileName = "Tusky_" + timeStamp + "_";
|
String imageFileName = "Tusky_" + randomId + "_";
|
||||||
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||||
return File.createTempFile(
|
return File.createTempFile(
|
||||||
imageFileName, /* prefix */
|
imageFileName, /* prefix */
|
||||||
@ -1088,12 +1087,12 @@ public final class ComposeActivity
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (type == QueuedMedia.Type.IMAGE &&
|
if (type == QueuedMedia.Type.IMAGE &&
|
||||||
(mediaSize > STATUS_MEDIA_SIZE_LIMIT || MediaUtils.getImageSquarePixels(getContentResolver(), item.uri) > STATUS_MEDIA_PIXEL_SIZE_LIMIT)) {
|
(mediaSize > STATUS_IMAGE_SIZE_LIMIT || MediaUtils.getImageSquarePixels(getContentResolver(), item.uri) > STATUS_IMAGE_PIXEL_SIZE_LIMIT)) {
|
||||||
downsizeMedia(item);
|
downsizeMedia(item);
|
||||||
} else {
|
} else {
|
||||||
uploadMedia(item);
|
uploadMedia(item);
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (IOException e) {
|
||||||
onUploadFailure(item, false);
|
onUploadFailure(item, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1223,14 +1222,17 @@ public final class ComposeActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void downsizeMedia(final QueuedMedia item) {
|
private void downsizeMedia(final QueuedMedia item) throws IOException {
|
||||||
item.readyStage = QueuedMedia.ReadyStage.DOWNSIZING;
|
item.readyStage = QueuedMedia.ReadyStage.DOWNSIZING;
|
||||||
|
|
||||||
new DownsizeImageTask(STATUS_MEDIA_SIZE_LIMIT, getContentResolver(),
|
new DownsizeImageTask(STATUS_IMAGE_SIZE_LIMIT, getContentResolver(), createNewImageFile(),
|
||||||
new DownsizeImageTask.Listener() {
|
new DownsizeImageTask.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<byte[]> contentList) {
|
public void onSuccess(File tempFile) {
|
||||||
item.content = contentList.get(0);
|
item.uri = FileProvider.getUriForFile(
|
||||||
|
ComposeActivity.this,
|
||||||
|
BuildConfig.APPLICATION_ID+".fileprovider",
|
||||||
|
tempFile);
|
||||||
uploadMedia(item);
|
uploadMedia(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1242,7 +1244,7 @@ public final class ComposeActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onMediaDownsizeFailure(QueuedMedia item) {
|
private void onMediaDownsizeFailure(QueuedMedia item) {
|
||||||
displayTransientError(R.string.error_media_upload_size);
|
displayTransientError(R.string.error_image_upload_size);
|
||||||
removeMediaFromQueue(item);
|
removeMediaFromQueue(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1258,32 +1260,20 @@ public final class ComposeActivity
|
|||||||
StringUtils.randomAlphanumericString(10),
|
StringUtils.randomAlphanumericString(10),
|
||||||
fileExtension);
|
fileExtension);
|
||||||
|
|
||||||
byte[] content = item.content;
|
|
||||||
|
|
||||||
if (content == null) {
|
|
||||||
InputStream stream;
|
InputStream stream;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream = getContentResolver().openInputStream(item.uri);
|
stream = getContentResolver().openInputStream(item.uri);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.d(TAG, Log.getStackTraceString(e));
|
Log.w(TAG, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = MediaUtils.inputStreamGetBytes(stream);
|
|
||||||
IOUtils.closeQuietly(stream);
|
|
||||||
|
|
||||||
if (content == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mimeType == null) mimeType = "multipart/form-data";
|
if (mimeType == null) mimeType = "multipart/form-data";
|
||||||
|
|
||||||
item.preview.setProgress(0);
|
item.preview.setProgress(0);
|
||||||
|
|
||||||
ProgressRequestBody fileBody = new ProgressRequestBody(content, MediaType.parse(mimeType),
|
ProgressRequestBody fileBody = new ProgressRequestBody(stream, MediaUtils.getMediaSize(getContentResolver(), item.uri), MediaType.parse(mimeType),
|
||||||
false, // If request body logging is enabled, pass true
|
|
||||||
new ProgressRequestBody.UploadCallback() { // may reference activity longer than I would like to
|
new ProgressRequestBody.UploadCallback() { // may reference activity longer than I would like to
|
||||||
int lastProgress = -1;
|
int lastProgress = -1;
|
||||||
|
|
||||||
@ -1378,8 +1368,8 @@ public final class ComposeActivity
|
|||||||
String topLevelType = mimeType.substring(0, mimeType.indexOf('/'));
|
String topLevelType = mimeType.substring(0, mimeType.indexOf('/'));
|
||||||
switch (topLevelType) {
|
switch (topLevelType) {
|
||||||
case "video": {
|
case "video": {
|
||||||
if (mediaSize > STATUS_MEDIA_SIZE_LIMIT) {
|
if (mediaSize > STATUS_VIDEO_SIZE_LIMIT) {
|
||||||
displayTransientError(R.string.error_media_upload_size);
|
displayTransientError(R.string.error_image_upload_size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mediaQueued.size() > 0
|
if (mediaQueued.size() > 0
|
||||||
@ -1543,7 +1533,6 @@ public final class ComposeActivity
|
|||||||
String id;
|
String id;
|
||||||
Call<Attachment> uploadRequest;
|
Call<Attachment> uploadRequest;
|
||||||
ReadyStage readyStage;
|
ReadyStage readyStage;
|
||||||
byte[] content;
|
|
||||||
long mediaSize;
|
long mediaSize;
|
||||||
String description;
|
String description;
|
||||||
|
|
||||||
|
@ -53,8 +53,8 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val AVATAR_SIZE = 400
|
const val AVATAR_SIZE = 400
|
||||||
const val HEADER_WIDTH = 700
|
const val HEADER_WIDTH = 1500
|
||||||
const val HEADER_HEIGHT = 335
|
const val HEADER_HEIGHT = 500
|
||||||
|
|
||||||
private const val AVATAR_PICK_RESULT = 1
|
private const val AVATAR_PICK_RESULT = 1
|
||||||
private const val HEADER_PICK_RESULT = 2
|
private const val HEADER_PICK_RESULT = 2
|
||||||
|
@ -124,6 +124,10 @@ public class PreferencesActivity extends BaseActivity
|
|||||||
restartActivitiesOnExit = true;
|
restartActivitiesOnExit = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "absoluteTimeView": {
|
||||||
|
restartActivitiesOnExit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "notificationsEnabled": {
|
case "notificationsEnabled": {
|
||||||
boolean enabled = sharedPreferences.getBoolean("notificationsEnabled", true);
|
boolean enabled = sharedPreferences.getBoolean("notificationsEnabled", true);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
@ -52,9 +52,11 @@ import com.keylesspalace.tusky.viewdata.NotificationViewData;
|
|||||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class NotificationsAdapter extends RecyclerView.Adapter {
|
public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||||
private static final int VIEW_TYPE_MENTION = 0;
|
private static final int VIEW_TYPE_MENTION = 0;
|
||||||
@ -66,6 +68,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
private StatusActionListener statusListener;
|
private StatusActionListener statusListener;
|
||||||
private NotificationActionListener notificationActionListener;
|
private NotificationActionListener notificationActionListener;
|
||||||
private boolean mediaPreviewEnabled;
|
private boolean mediaPreviewEnabled;
|
||||||
|
private boolean useAbsoluteTime;
|
||||||
private BidiFormatter bidiFormatter;
|
private BidiFormatter bidiFormatter;
|
||||||
|
|
||||||
public NotificationsAdapter(StatusActionListener statusListener,
|
public NotificationsAdapter(StatusActionListener statusListener,
|
||||||
@ -75,6 +78,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
this.statusListener = statusListener;
|
this.statusListener = statusListener;
|
||||||
this.notificationActionListener = notificationActionListener;
|
this.notificationActionListener = notificationActionListener;
|
||||||
mediaPreviewEnabled = true;
|
mediaPreviewEnabled = true;
|
||||||
|
useAbsoluteTime = false;
|
||||||
bidiFormatter = BidiFormatter.getInstance();
|
bidiFormatter = BidiFormatter.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,12 +90,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
case VIEW_TYPE_MENTION: {
|
case VIEW_TYPE_MENTION: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.item_status, parent, false);
|
.inflate(R.layout.item_status, parent, false);
|
||||||
return new StatusViewHolder(view);
|
return new StatusViewHolder(view, useAbsoluteTime);
|
||||||
}
|
}
|
||||||
case VIEW_TYPE_STATUS_NOTIFICATION: {
|
case VIEW_TYPE_STATUS_NOTIFICATION: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.item_status_notification, parent, false);
|
.inflate(R.layout.item_status_notification, parent, false);
|
||||||
return new StatusNotificationViewHolder(view);
|
return new StatusNotificationViewHolder(view, useAbsoluteTime);
|
||||||
}
|
}
|
||||||
case VIEW_TYPE_FOLLOW: {
|
case VIEW_TYPE_FOLLOW: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
@ -230,6 +234,10 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
return mediaPreviewEnabled;
|
return mediaPreviewEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUseAbsoluteTime(boolean useAbsoluteTime) {
|
||||||
|
this.useAbsoluteTime = useAbsoluteTime;
|
||||||
|
}
|
||||||
|
|
||||||
public interface NotificationActionListener {
|
public interface NotificationActionListener {
|
||||||
void onViewAccount(String id);
|
void onViewAccount(String id);
|
||||||
|
|
||||||
@ -317,7 +325,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
private NotificationActionListener notificationActionListener;
|
private NotificationActionListener notificationActionListener;
|
||||||
private StatusViewData.Concrete statusViewData;
|
private StatusViewData.Concrete statusViewData;
|
||||||
|
|
||||||
StatusNotificationViewHolder(View itemView) {
|
private boolean useAbsoluteTime;
|
||||||
|
private SimpleDateFormat shortSdf;
|
||||||
|
private SimpleDateFormat longSdf;
|
||||||
|
|
||||||
|
StatusNotificationViewHolder(View itemView, boolean useAbsoluteTime) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
message = itemView.findViewById(R.id.notification_top_text);
|
message = itemView.findViewById(R.id.notification_top_text);
|
||||||
statusNameBar = itemView.findViewById(R.id.status_name_bar);
|
statusNameBar = itemView.findViewById(R.id.status_name_bar);
|
||||||
@ -340,6 +352,10 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
message.setOnClickListener(this);
|
message.setOnClickListener(this);
|
||||||
statusContent.setOnClickListener(this);
|
statusContent.setOnClickListener(this);
|
||||||
contentWarningButton.setOnCheckedChangeListener(this);
|
contentWarningButton.setOnCheckedChangeListener(this);
|
||||||
|
|
||||||
|
this.useAbsoluteTime = useAbsoluteTime;
|
||||||
|
shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||||
|
longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNotificationContent(boolean show) {
|
private void showNotificationContent(boolean show) {
|
||||||
@ -363,7 +379,20 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
username.setText(usernameText);
|
username.setText(usernameText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCreatedAt(@Nullable Date createdAt) {
|
protected void setCreatedAt(@Nullable Date createdAt) {
|
||||||
|
if (useAbsoluteTime) {
|
||||||
|
String time;
|
||||||
|
if (createdAt != null) {
|
||||||
|
if (System.currentTimeMillis() - createdAt.getTime() > 86400000L) {
|
||||||
|
time = longSdf.format(createdAt);
|
||||||
|
} else {
|
||||||
|
time = shortSdf.format(createdAt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
time = "??:??:??";
|
||||||
|
}
|
||||||
|
timestampInfo.setText(time);
|
||||||
|
} else {
|
||||||
// This is the visible timestampInfo.
|
// This is the visible timestampInfo.
|
||||||
String readout;
|
String readout;
|
||||||
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
||||||
@ -384,6 +413,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
timestampInfo.setText(readout);
|
timestampInfo.setText(readout);
|
||||||
timestampInfo.setContentDescription(readoutAloud);
|
timestampInfo.setContentDescription(readoutAloud);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setMessage(NotificationViewData.Concrete notificationViewData, LinkListener listener, BidiFormatter bidiFormatter) {
|
void setMessage(NotificationViewData.Concrete notificationViewData, LinkListener listener, BidiFormatter bidiFormatter) {
|
||||||
this.statusViewData = notificationViewData.getStatusViewData();
|
this.statusViewData = notificationViewData.getStatusViewData();
|
||||||
@ -469,10 +499,12 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||||||
switch (v.getId()) {
|
switch (v.getId()) {
|
||||||
case R.id.notification_container:
|
case R.id.notification_container:
|
||||||
case R.id.notification_content:
|
case R.id.notification_content:
|
||||||
if (notificationActionListener != null) notificationActionListener.onViewStatusForNotificationId(notificationId);
|
if (notificationActionListener != null)
|
||||||
|
notificationActionListener.onViewStatusForNotificationId(notificationId);
|
||||||
break;
|
break;
|
||||||
case R.id.notification_top_text:
|
case R.id.notification_top_text:
|
||||||
if (notificationActionListener != null) notificationActionListener.onViewAccount(accountId);
|
if (notificationActionListener != null)
|
||||||
|
notificationActionListener.onViewAccount(accountId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
|||||||
private boolean mediaPreviewsEnabled;
|
private boolean mediaPreviewsEnabled;
|
||||||
private boolean alwaysShowSensitiveMedia;
|
private boolean alwaysShowSensitiveMedia;
|
||||||
private boolean collapseLongStatusContent;
|
private boolean collapseLongStatusContent;
|
||||||
|
private boolean useAbsoluteTime;
|
||||||
|
|
||||||
private LinkListener linkListener;
|
private LinkListener linkListener;
|
||||||
private StatusActionListener statusListener;
|
private StatusActionListener statusListener;
|
||||||
@ -57,7 +58,8 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
|||||||
boolean alwaysShowSensitiveMedia,
|
boolean alwaysShowSensitiveMedia,
|
||||||
boolean collapseLongStatusContent,
|
boolean collapseLongStatusContent,
|
||||||
LinkListener linkListener,
|
LinkListener linkListener,
|
||||||
StatusActionListener statusListener) {
|
StatusActionListener statusListener,
|
||||||
|
boolean useAbsoluteTime) {
|
||||||
|
|
||||||
this.accountList = Collections.emptyList();
|
this.accountList = Collections.emptyList();
|
||||||
this.statusList = Collections.emptyList();
|
this.statusList = Collections.emptyList();
|
||||||
@ -67,6 +69,7 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
|||||||
this.mediaPreviewsEnabled = mediaPreviewsEnabled;
|
this.mediaPreviewsEnabled = mediaPreviewsEnabled;
|
||||||
this.alwaysShowSensitiveMedia = alwaysShowSensitiveMedia;
|
this.alwaysShowSensitiveMedia = alwaysShowSensitiveMedia;
|
||||||
this.collapseLongStatusContent = collapseLongStatusContent;
|
this.collapseLongStatusContent = collapseLongStatusContent;
|
||||||
|
this.useAbsoluteTime = useAbsoluteTime;
|
||||||
|
|
||||||
this.linkListener = linkListener;
|
this.linkListener = linkListener;
|
||||||
this.statusListener = statusListener;
|
this.statusListener = statusListener;
|
||||||
@ -91,7 +94,7 @@ public class SearchResultsAdapter extends RecyclerView.Adapter {
|
|||||||
case VIEW_TYPE_STATUS: {
|
case VIEW_TYPE_STATUS: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.item_status, parent, false);
|
.inflate(R.layout.item_status, parent, false);
|
||||||
return new StatusViewHolder(view);
|
return new StatusViewHolder(view, useAbsoluteTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,10 @@ import com.keylesspalace.tusky.viewdata.StatusViewData;
|
|||||||
import com.mikepenz.iconics.utils.Utils;
|
import com.mikepenz.iconics.utils.Utils;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import at.connyduck.sparkbutton.SparkButton;
|
import at.connyduck.sparkbutton.SparkButton;
|
||||||
import at.connyduck.sparkbutton.SparkEventListener;
|
import at.connyduck.sparkbutton.SparkEventListener;
|
||||||
@ -67,7 +69,11 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||||||
TextView content;
|
TextView content;
|
||||||
TextView contentWarningDescription;
|
TextView contentWarningDescription;
|
||||||
|
|
||||||
StatusBaseViewHolder(View itemView) {
|
private boolean useAbsoluteTime;
|
||||||
|
private SimpleDateFormat shortSdf;
|
||||||
|
private SimpleDateFormat longSdf;
|
||||||
|
|
||||||
|
StatusBaseViewHolder(View itemView, boolean useAbsoluteTime) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
container = itemView.findViewById(R.id.status_container);
|
container = itemView.findViewById(R.id.status_container);
|
||||||
displayName = itemView.findViewById(R.id.status_display_name);
|
displayName = itemView.findViewById(R.id.status_display_name);
|
||||||
@ -95,6 +101,10 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||||||
contentWarningDescription = itemView.findViewById(R.id.status_content_warning_description);
|
contentWarningDescription = itemView.findViewById(R.id.status_content_warning_description);
|
||||||
contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
|
contentWarningButton = itemView.findViewById(R.id.status_content_warning_button);
|
||||||
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
|
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);
|
||||||
|
|
||||||
|
this.useAbsoluteTime = useAbsoluteTime;
|
||||||
|
shortSdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||||
|
longSdf = new SimpleDateFormat("MM/dd HH:mm:ss", Locale.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int getMediaPreviewHeight(Context context);
|
protected abstract int getMediaPreviewHeight(Context context);
|
||||||
@ -130,6 +140,19 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void setCreatedAt(@Nullable Date createdAt) {
|
protected void setCreatedAt(@Nullable Date createdAt) {
|
||||||
|
if (useAbsoluteTime) {
|
||||||
|
String time;
|
||||||
|
if (createdAt != null) {
|
||||||
|
if (System.currentTimeMillis() - createdAt.getTime() > 86400000L) {
|
||||||
|
time = longSdf.format(createdAt);
|
||||||
|
} else {
|
||||||
|
time = shortSdf.format(createdAt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
time = "??:??:??";
|
||||||
|
}
|
||||||
|
timestampInfo.setText(time);
|
||||||
|
} else {
|
||||||
// This is the visible timestampInfo.
|
// This is the visible timestampInfo.
|
||||||
String readout;
|
String readout;
|
||||||
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
/* This one is for screen-readers. Frequently, they would mispronounce timestamps like "17m"
|
||||||
@ -150,6 +173,7 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||||||
timestampInfo.setText(readout);
|
timestampInfo.setText(readout);
|
||||||
timestampInfo.setContentDescription(readoutAloud);
|
timestampInfo.setContentDescription(readoutAloud);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void showContent(boolean show) {
|
protected void showContent(boolean show) {
|
||||||
if (show) {
|
if (show) {
|
||||||
|
@ -41,7 +41,7 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
|||||||
private TextView cardUrl;
|
private TextView cardUrl;
|
||||||
|
|
||||||
StatusDetailedViewHolder(View view) {
|
StatusDetailedViewHolder(View view) {
|
||||||
super(view);
|
super(view, false);
|
||||||
reblogs = view.findViewById(R.id.status_reblogs);
|
reblogs = view.findViewById(R.id.status_reblogs);
|
||||||
favourites = view.findViewById(R.id.status_favourites);
|
favourites = view.findViewById(R.id.status_favourites);
|
||||||
cardView = view.findViewById(R.id.card_view);
|
cardView = view.findViewById(R.id.card_view);
|
||||||
|
@ -34,8 +34,8 @@ public class StatusViewHolder extends StatusBaseViewHolder {
|
|||||||
private ImageView avatarReblog;
|
private ImageView avatarReblog;
|
||||||
private TextView rebloggedBar;
|
private TextView rebloggedBar;
|
||||||
|
|
||||||
StatusViewHolder(View itemView) {
|
StatusViewHolder(View itemView, boolean useAbsoluteTime) {
|
||||||
super(itemView);
|
super(itemView, useAbsoluteTime);
|
||||||
avatarReblog = itemView.findViewById(R.id.status_avatar_reblog);
|
avatarReblog = itemView.findViewById(R.id.status_avatar_reblog);
|
||||||
rebloggedBar = itemView.findViewById(R.id.status_reblogged);
|
rebloggedBar = itemView.findViewById(R.id.status_reblogged);
|
||||||
//workaround because Android < API 21 does not support setting drawableLeft from xml when it is a vector image
|
//workaround because Android < API 21 does not support setting drawableLeft from xml when it is a vector image
|
||||||
|
@ -36,12 +36,14 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
|||||||
private List<StatusViewData.Concrete> statuses;
|
private List<StatusViewData.Concrete> statuses;
|
||||||
private StatusActionListener statusActionListener;
|
private StatusActionListener statusActionListener;
|
||||||
private boolean mediaPreviewEnabled;
|
private boolean mediaPreviewEnabled;
|
||||||
|
private boolean useAbsoluteTime;
|
||||||
private int detailedStatusPosition;
|
private int detailedStatusPosition;
|
||||||
|
|
||||||
public ThreadAdapter(StatusActionListener listener) {
|
public ThreadAdapter(StatusActionListener listener) {
|
||||||
this.statusActionListener = listener;
|
this.statusActionListener = listener;
|
||||||
this.statuses = new ArrayList<>();
|
this.statuses = new ArrayList<>();
|
||||||
mediaPreviewEnabled = true;
|
mediaPreviewEnabled = true;
|
||||||
|
useAbsoluteTime = false;
|
||||||
detailedStatusPosition = RecyclerView.NO_POSITION;
|
detailedStatusPosition = RecyclerView.NO_POSITION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
|||||||
case VIEW_TYPE_STATUS: {
|
case VIEW_TYPE_STATUS: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.item_status, parent, false);
|
.inflate(R.layout.item_status, parent, false);
|
||||||
return new StatusViewHolder(view);
|
return new StatusViewHolder(view, useAbsoluteTime);
|
||||||
}
|
}
|
||||||
case VIEW_TYPE_STATUS_DETAILED: {
|
case VIEW_TYPE_STATUS_DETAILED: {
|
||||||
View view = LayoutInflater.from(parent.getContext())
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
@ -149,6 +151,10 @@ public class ThreadAdapter extends RecyclerView.Adapter {
|
|||||||
mediaPreviewEnabled = enabled;
|
mediaPreviewEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUseAbsoluteTime(boolean useAbsoluteTime) {
|
||||||
|
this.useAbsoluteTime = useAbsoluteTime;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDetailedStatusPosition(int position) {
|
public void setDetailedStatusPosition(int position) {
|
||||||
if (position != detailedStatusPosition
|
if (position != detailedStatusPosition
|
||||||
&& detailedStatusPosition != RecyclerView.NO_POSITION) {
|
&& detailedStatusPosition != RecyclerView.NO_POSITION) {
|
||||||
|
@ -39,6 +39,7 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
|||||||
private final AdapterDataSource<StatusViewData> dataSource;
|
private final AdapterDataSource<StatusViewData> dataSource;
|
||||||
private final StatusActionListener statusListener;
|
private final StatusActionListener statusListener;
|
||||||
private boolean mediaPreviewEnabled;
|
private boolean mediaPreviewEnabled;
|
||||||
|
private boolean useAbsoluteTime;
|
||||||
|
|
||||||
public TimelineAdapter(AdapterDataSource<StatusViewData> dataSource,
|
public TimelineAdapter(AdapterDataSource<StatusViewData> dataSource,
|
||||||
StatusActionListener statusListener) {
|
StatusActionListener statusListener) {
|
||||||
@ -46,6 +47,7 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
|||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.statusListener = statusListener;
|
this.statusListener = statusListener;
|
||||||
mediaPreviewEnabled = true;
|
mediaPreviewEnabled = true;
|
||||||
|
useAbsoluteTime = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -56,7 +58,7 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
|||||||
case VIEW_TYPE_STATUS: {
|
case VIEW_TYPE_STATUS: {
|
||||||
View view = LayoutInflater.from(viewGroup.getContext())
|
View view = LayoutInflater.from(viewGroup.getContext())
|
||||||
.inflate(R.layout.item_status, viewGroup, false);
|
.inflate(R.layout.item_status, viewGroup, false);
|
||||||
return new StatusViewHolder(view);
|
return new StatusViewHolder(view, useAbsoluteTime);
|
||||||
}
|
}
|
||||||
case VIEW_TYPE_PLACEHOLDER: {
|
case VIEW_TYPE_PLACEHOLDER: {
|
||||||
View view = LayoutInflater.from(viewGroup.getContext())
|
View view = LayoutInflater.from(viewGroup.getContext())
|
||||||
@ -97,6 +99,10 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
|||||||
mediaPreviewEnabled = enabled;
|
mediaPreviewEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUseAbsoluteTime(boolean useAbsoluteTime){
|
||||||
|
this.useAbsoluteTime=useAbsoluteTime;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getMediaPreviewEnabled() {
|
public boolean getMediaPreviewEnabled() {
|
||||||
return mediaPreviewEnabled;
|
return mediaPreviewEnabled;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +202,8 @@ public class NotificationsFragment extends SFragment implements
|
|||||||
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
||||||
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
||||||
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
||||||
|
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||||
|
adapter.setUseAbsoluteTime(useAbsoluteTime);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
notifications.clear();
|
notifications.clear();
|
||||||
|
@ -51,7 +51,7 @@ class SearchFragment : SFragment(), StatusActionListener, Injectable {
|
|||||||
private var alwaysShowSensitiveMedia = false
|
private var alwaysShowSensitiveMedia = false
|
||||||
private var mediaPreviewEnabled = true
|
private var mediaPreviewEnabled = true
|
||||||
private var collapseLongStatusContent = true;
|
private var collapseLongStatusContent = true;
|
||||||
|
private var useAbsoluteTime = false
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_search, container, false)
|
return inflater.inflate(R.layout.fragment_search, container, false)
|
||||||
@ -61,6 +61,7 @@ class SearchFragment : SFragment(), StatusActionListener, Injectable {
|
|||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(view.context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(view.context)
|
||||||
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false)
|
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false)
|
||||||
mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true)
|
mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true)
|
||||||
|
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false)
|
||||||
|
|
||||||
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
||||||
|
|
||||||
@ -71,8 +72,7 @@ class SearchFragment : SFragment(), StatusActionListener, Injectable {
|
|||||||
alwaysShowSensitiveMedia,
|
alwaysShowSensitiveMedia,
|
||||||
collapseLongStatusContent,
|
collapseLongStatusContent,
|
||||||
this,
|
this,
|
||||||
this
|
useAbsoluteTime)
|
||||||
)
|
|
||||||
searchRecyclerView.adapter = searchAdapter
|
searchRecyclerView.adapter = searchAdapter
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -249,6 +249,8 @@ public class TimelineFragment extends SFragment implements
|
|||||||
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
|
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
|
||||||
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
||||||
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
||||||
|
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||||
|
adapter.setUseAbsoluteTime(useAbsoluteTime);
|
||||||
|
|
||||||
boolean filter = preferences.getBoolean("tabFilterHomeReplies", true);
|
boolean filter = preferences.getBoolean("tabFilterHomeReplies", true);
|
||||||
filterRemoveReplies = kind == Kind.HOME && !filter;
|
filterRemoveReplies = kind == Kind.HOME && !filter;
|
||||||
|
@ -163,6 +163,8 @@ public final class ViewThreadFragment extends SFragment implements
|
|||||||
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
collapseLongStatusContent = preferences.getBoolean("collapseLongStatuses", true);
|
||||||
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
|
||||||
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
|
||||||
|
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
|
||||||
|
adapter.setUseAbsoluteTime(useAbsoluteTime);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
statuses.clear();
|
statuses.clear();
|
||||||
|
@ -17,18 +17,18 @@ package com.keylesspalace.tusky.network;
|
|||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import okio.BufferedSink;
|
import okio.BufferedSink;
|
||||||
|
|
||||||
public final class ProgressRequestBody extends RequestBody {
|
public final class ProgressRequestBody extends RequestBody {
|
||||||
private final byte[] content;
|
private final InputStream content;
|
||||||
private final UploadCallback mListener;
|
private final long contentLength;
|
||||||
|
private final UploadCallback uploadListener;
|
||||||
private final MediaType mediaType;
|
private final MediaType mediaType;
|
||||||
private boolean shouldIgnoreThisPass;
|
|
||||||
|
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 2048;
|
private static final int DEFAULT_BUFFER_SIZE = 2048;
|
||||||
|
|
||||||
@ -36,11 +36,11 @@ public final class ProgressRequestBody extends RequestBody {
|
|||||||
void onProgressUpdate(int percentage);
|
void onProgressUpdate(int percentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgressRequestBody(final byte[] content, final MediaType mediaType, boolean shouldIgnoreFirst, final UploadCallback listener) {
|
public ProgressRequestBody(final InputStream content, long contentLength, final MediaType mediaType, final UploadCallback listener) {
|
||||||
this.content = content;
|
this.content = content;
|
||||||
|
this.contentLength = contentLength;
|
||||||
this.mediaType = mediaType;
|
this.mediaType = mediaType;
|
||||||
mListener = listener;
|
this.uploadListener = listener;
|
||||||
shouldIgnoreThisPass = shouldIgnoreFirst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -50,29 +50,25 @@ public final class ProgressRequestBody extends RequestBody {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long contentLength() {
|
public long contentLength() {
|
||||||
return content.length;
|
return contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
||||||
long length = content.length;
|
|
||||||
|
|
||||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(content);
|
|
||||||
long uploaded = 0;
|
long uploaded = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int read;
|
int read;
|
||||||
while ((read = in.read(buffer)) != -1) {
|
while ((read = content.read(buffer)) != -1) {
|
||||||
if (!shouldIgnoreThisPass) {
|
uploadListener.onProgressUpdate((int)(100 * uploaded / contentLength));
|
||||||
mListener.onProgressUpdate((int)(100 * uploaded / length));
|
|
||||||
}
|
|
||||||
uploaded += read;
|
uploaded += read;
|
||||||
sink.write(buffer, 0, read);
|
sink.write(buffer, 0, read);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
in.close();
|
content.close();
|
||||||
}
|
}
|
||||||
shouldIgnoreThisPass = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,11 +21,11 @@ import android.graphics.BitmapFactory;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces the file size of images to fit under a given limit by resizing them, maintaining both
|
* Reduces the file size of images to fit under a given limit by resizing them, maintaining both
|
||||||
@ -35,22 +35,23 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
private int sizeLimit;
|
private int sizeLimit;
|
||||||
private ContentResolver contentResolver;
|
private ContentResolver contentResolver;
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
private List<byte[]> resultList;
|
private File tempFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param sizeLimit the maximum number of bytes each image can take
|
* @param sizeLimit the maximum number of bytes each image can take
|
||||||
* @param contentResolver to resolve the specified images' URIs
|
* @param contentResolver to resolve the specified images' URIs
|
||||||
|
* @param tempFile the file where the result will be stored
|
||||||
* @param listener to whom the results are given
|
* @param listener to whom the results are given
|
||||||
*/
|
*/
|
||||||
public DownsizeImageTask(int sizeLimit, ContentResolver contentResolver, Listener listener) {
|
public DownsizeImageTask(int sizeLimit, ContentResolver contentResolver, File tempFile, Listener listener) {
|
||||||
this.sizeLimit = sizeLimit;
|
this.sizeLimit = sizeLimit;
|
||||||
this.contentResolver = contentResolver;
|
this.contentResolver = contentResolver;
|
||||||
|
this.tempFile = tempFile;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Uri... uris) {
|
protected Boolean doInBackground(Uri... uris) {
|
||||||
resultList = new ArrayList<>();
|
|
||||||
for (Uri uri : uris) {
|
for (Uri uri : uris) {
|
||||||
InputStream inputStream;
|
InputStream inputStream;
|
||||||
try {
|
try {
|
||||||
@ -65,8 +66,6 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
IOUtils.closeQuietly(inputStream);
|
IOUtils.closeQuietly(inputStream);
|
||||||
// Get EXIF data, for orientation info.
|
// Get EXIF data, for orientation info.
|
||||||
int orientation = MediaUtils.getImageOrientation(uri, contentResolver);
|
int orientation = MediaUtils.getImageOrientation(uri, contentResolver);
|
||||||
// Then use that information to determine how much to compress.
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
/* Unfortunately, there isn't a determined worst case compression ratio for image
|
/* Unfortunately, there isn't a determined worst case compression ratio for image
|
||||||
* formats. So, the only way to tell if they're too big is to compress them and
|
* formats. So, the only way to tell if they're too big is to compress them and
|
||||||
* test, and keep trying at smaller sizes. The initial estimate should be good for
|
* test, and keep trying at smaller sizes. The initial estimate should be good for
|
||||||
@ -74,7 +73,12 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
* sure it gets downsized to below the limit. */
|
* sure it gets downsized to below the limit. */
|
||||||
int scaledImageSize = 1024;
|
int scaledImageSize = 1024;
|
||||||
do {
|
do {
|
||||||
stream.reset();
|
OutputStream stream;
|
||||||
|
try {
|
||||||
|
stream = new FileOutputStream(tempFile);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
inputStream = contentResolver.openInputStream(uri);
|
inputStream = contentResolver.openInputStream(uri);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
@ -109,9 +113,8 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
reorientedBitmap.compress(format, 85, stream);
|
reorientedBitmap.compress(format, 85, stream);
|
||||||
reorientedBitmap.recycle();
|
reorientedBitmap.recycle();
|
||||||
scaledImageSize /= 2;
|
scaledImageSize /= 2;
|
||||||
} while (stream.size() > sizeLimit);
|
} while (tempFile.length() > sizeLimit);
|
||||||
|
|
||||||
resultList.add(stream.toByteArray());
|
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -122,7 +125,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean successful) {
|
protected void onPostExecute(Boolean successful) {
|
||||||
if (successful) {
|
if (successful) {
|
||||||
listener.onSuccess(resultList);
|
listener.onSuccess(tempFile);
|
||||||
} else {
|
} else {
|
||||||
listener.onFailure();
|
listener.onFailure();
|
||||||
}
|
}
|
||||||
@ -131,7 +134,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
|||||||
|
|
||||||
/** Used to communicate the results of the task. */
|
/** Used to communicate the results of the task. */
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
void onSuccess(List<byte[]> contentList);
|
void onSuccess(File file);
|
||||||
void onFailure();
|
void onFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">تم رفض التصريح.</string>
|
<string name="error_authorization_denied">تم رفض التصريح.</string>
|
||||||
<string name="error_retrieving_oauth_token">فشل الحصول على رمز الدخول.</string>
|
<string name="error_retrieving_oauth_token">فشل الحصول على رمز الدخول.</string>
|
||||||
<string name="error_compose_character_limit">المنشور طويل جدا !</string>
|
<string name="error_compose_character_limit">المنشور طويل جدا !</string>
|
||||||
<string name="error_media_upload_size">يجب أن يكون حجم الملف أقل من 4 ميغابايت.</string>
|
<string name="error_image_upload_size">يجب أن يكون حجم الملف أقل من 4 ميغابايت.</string>
|
||||||
<string name="error_media_upload_type">لا يمكن رفع هذا النوع من الملفات.</string>
|
<string name="error_media_upload_type">لا يمكن رفع هذا النوع من الملفات.</string>
|
||||||
<string name="error_media_upload_opening">تعذر فتح ذاك الملف.</string>
|
<string name="error_media_upload_opening">تعذر فتح ذاك الملف.</string>
|
||||||
<string name="error_media_upload_permission">التصريح لازم لقراءة الوسائط</string>
|
<string name="error_media_upload_permission">التصريح لازم لقراءة الوسائط</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">L\'autorització s\'ha denegat.</string>
|
<string name="error_authorization_denied">L\'autorització s\'ha denegat.</string>
|
||||||
<string name="error_retrieving_oauth_token">L\'obtenció del testimoni d\'inici de sessió ha fallat.</string>
|
<string name="error_retrieving_oauth_token">L\'obtenció del testimoni d\'inici de sessió ha fallat.</string>
|
||||||
<string name="error_compose_character_limit">L\'estat és massa llarg!</string>
|
<string name="error_compose_character_limit">L\'estat és massa llarg!</string>
|
||||||
<string name="error_media_upload_size">El fitxer ha de ser inferior a 8MB.</string>
|
<string name="error_image_upload_size">El fitxer ha de ser inferior a 8MB.</string>
|
||||||
<string name="error_media_upload_type">Aquest tipus de fitxer no es pot pujar.</string>
|
<string name="error_media_upload_type">Aquest tipus de fitxer no es pot pujar.</string>
|
||||||
<string name="error_media_upload_opening">Aquest tipus de fitxer no es pot obrir.</string>
|
<string name="error_media_upload_opening">Aquest tipus de fitxer no es pot obrir.</string>
|
||||||
<string name="error_media_upload_permission">Cal permís de lectura del mitjà.</string>
|
<string name="error_media_upload_permission">Cal permís de lectura del mitjà.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Autorisierung fehlgeschlagen.</string>
|
<string name="error_authorization_denied">Autorisierung fehlgeschlagen.</string>
|
||||||
<string name="error_retrieving_oauth_token">Es konnte kein Login-Token abgerufen werden.</string>
|
<string name="error_retrieving_oauth_token">Es konnte kein Login-Token abgerufen werden.</string>
|
||||||
<string name="error_compose_character_limit">Der Beitrag ist zu lang!</string>
|
<string name="error_compose_character_limit">Der Beitrag ist zu lang!</string>
|
||||||
<string name="error_media_upload_size">Die Datei muss kleiner als 8MB sein.</string>
|
<string name="error_image_upload_size">Die Datei muss kleiner als 8MB sein.</string>
|
||||||
<string name="error_media_upload_type">Dieser Dateityp darf nicht hochgeladen werden.</string>
|
<string name="error_media_upload_type">Dieser Dateityp darf nicht hochgeladen werden.</string>
|
||||||
<string name="error_media_upload_opening">Die Datei konnte nicht geöffnet werden.</string>
|
<string name="error_media_upload_opening">Die Datei konnte nicht geöffnet werden.</string>
|
||||||
<string name="error_media_upload_permission">Eine Leseberechtigung wird für das Hochladen der Mediendatei benötigt.</string>
|
<string name="error_media_upload_permission">Eine Leseberechtigung wird für das Hochladen der Mediendatei benötigt.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">La autorización falló.</string>
|
<string name="error_authorization_denied">La autorización falló.</string>
|
||||||
<string name="error_retrieving_oauth_token">Fallo al obtener identificador de login.</string>
|
<string name="error_retrieving_oauth_token">Fallo al obtener identificador de login.</string>
|
||||||
<string name="error_compose_character_limit">¡El estado es demasiado largo!</string>
|
<string name="error_compose_character_limit">¡El estado es demasiado largo!</string>
|
||||||
<string name="error_media_upload_size">El archivo debe ser inferior a 8MB.</string>
|
<string name="error_image_upload_size">El archivo debe ser inferior a 8MB.</string>
|
||||||
<string name="error_media_upload_type">No se admite este tipo de archivo.</string>
|
<string name="error_media_upload_type">No se admite este tipo de archivo.</string>
|
||||||
<string name="error_media_upload_opening">No pudo abrirse el fichero.</string>
|
<string name="error_media_upload_opening">No pudo abrirse el fichero.</string>
|
||||||
<string name="error_media_upload_permission">Se requiere permiso para acceder al almacenamiento.</string>
|
<string name="error_media_upload_permission">Se requiere permiso para acceder al almacenamiento.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Authentification refusée.</string>
|
<string name="error_authorization_denied">Authentification refusée.</string>
|
||||||
<string name="error_retrieving_oauth_token">Impossible de récupérer le jeton d’authentification.</string>
|
<string name="error_retrieving_oauth_token">Impossible de récupérer le jeton d’authentification.</string>
|
||||||
<string name="error_compose_character_limit">Votre pouet est trop long !</string>
|
<string name="error_compose_character_limit">Votre pouet est trop long !</string>
|
||||||
<string name="error_media_upload_size">Le fichier doit peser moins de 8 Mo.</string>
|
<string name="error_image_upload_size">Le fichier doit peser moins de 8 Mo.</string>
|
||||||
<string name="error_media_upload_type">Ce type de fichier n’est pas accepté.</string>
|
<string name="error_media_upload_type">Ce type de fichier n’est pas accepté.</string>
|
||||||
<string name="error_media_upload_opening">Le fichier ne peut pas être ouvert.</string>
|
<string name="error_media_upload_opening">Le fichier ne peut pas être ouvert.</string>
|
||||||
<string name="error_media_upload_permission">Permission requise pour lire ce média.</string>
|
<string name="error_media_upload_permission">Permission requise pour lire ce média.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Engedélyezés letiltva.</string>
|
<string name="error_authorization_denied">Engedélyezés letiltva.</string>
|
||||||
<string name="error_retrieving_oauth_token">Bejelentkezési token megszerzése sikertelen.</string>
|
<string name="error_retrieving_oauth_token">Bejelentkezési token megszerzése sikertelen.</string>
|
||||||
<string name="error_compose_character_limit">Túl hosszú a tülkölés!</string>
|
<string name="error_compose_character_limit">Túl hosszú a tülkölés!</string>
|
||||||
<string name="error_media_upload_size">A fájl kisebb kell legyen mint 8MB.</string>
|
<string name="error_image_upload_size">A fájl kisebb kell legyen mint 8MB.</string>
|
||||||
<string name="error_media_upload_type">Fájl feltöltése sikertelen.</string>
|
<string name="error_media_upload_type">Fájl feltöltése sikertelen.</string>
|
||||||
<string name="error_media_upload_opening">Fájl megnyitása sikertelen.</string>
|
<string name="error_media_upload_opening">Fájl megnyitása sikertelen.</string>
|
||||||
<string name="error_media_upload_permission">Média olvasási engedély szükséges.</string>
|
<string name="error_media_upload_permission">Média olvasási engedély szükséges.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">L\'autorizzazione è stata negata.</string>
|
<string name="error_authorization_denied">L\'autorizzazione è stata negata.</string>
|
||||||
<string name="error_retrieving_oauth_token">Errore nell\'acquisizione del token di accesso.</string>
|
<string name="error_retrieving_oauth_token">Errore nell\'acquisizione del token di accesso.</string>
|
||||||
<string name="error_compose_character_limit">Lo stato è troppo lungo!</string>
|
<string name="error_compose_character_limit">Lo stato è troppo lungo!</string>
|
||||||
<string name="error_media_upload_size">La dimensione del file deve essere inferiore a 8MB.</string>
|
<string name="error_image_upload_size">La dimensione del file deve essere inferiore a 8MB.</string>
|
||||||
<string name="error_media_upload_type">Questo tipo di file non può essere caricato.</string>
|
<string name="error_media_upload_type">Questo tipo di file non può essere caricato.</string>
|
||||||
<string name="error_media_upload_opening">Questo file non può essere aperto.</string>
|
<string name="error_media_upload_opening">Questo file non può essere aperto.</string>
|
||||||
<string name="error_media_upload_permission">Il permesso di lettura della scheda sd è richiesto.</string>
|
<string name="error_media_upload_permission">Il permesso di lettura della scheda sd è richiesto.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">承認が拒否されました。</string>
|
<string name="error_authorization_denied">承認が拒否されました。</string>
|
||||||
<string name="error_retrieving_oauth_token">ログイントークンの取得に失敗しました。</string>
|
<string name="error_retrieving_oauth_token">ログイントークンの取得に失敗しました。</string>
|
||||||
<string name="error_compose_character_limit">投稿文が長すぎます!</string>
|
<string name="error_compose_character_limit">投稿文が長すぎます!</string>
|
||||||
<string name="error_media_upload_size">ファイルは4MB未満にしてください。</string>
|
<string name="error_image_upload_size">ファイルは4MB未満にしてください。</string>
|
||||||
<string name="error_media_upload_type">その形式のファイルはアップロードできません。</string>
|
<string name="error_media_upload_type">その形式のファイルはアップロードできません。</string>
|
||||||
<string name="error_media_upload_opening">ファイルを開けませんでした。</string>
|
<string name="error_media_upload_opening">ファイルを開けませんでした。</string>
|
||||||
<string name="error_media_upload_permission">メディアの読み取り許可が必要です。</string>
|
<string name="error_media_upload_permission">メディアの読み取り許可が必要です。</string>
|
||||||
@ -277,4 +277,6 @@
|
|||||||
<string name="action_set_caption">説明を設定</string>
|
<string name="action_set_caption">説明を設定</string>
|
||||||
<string name="action_remove_media">消去</string>
|
<string name="action_remove_media">消去</string>
|
||||||
|
|
||||||
|
<string name="pref_title_absolute_time">絶対時間で表示</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<string name="error_authorization_denied">인증이 거부되었습니다.</string>
|
<string name="error_authorization_denied">인증이 거부되었습니다.</string>
|
||||||
<string name="error_retrieving_oauth_token">로그인 토큰을 가져오는 데 실패했습니다.</string>
|
<string name="error_retrieving_oauth_token">로그인 토큰을 가져오는 데 실패했습니다.</string>
|
||||||
<string name="error_compose_character_limit">툿이 너무 깁니다!</string>
|
<string name="error_compose_character_limit">툿이 너무 깁니다!</string>
|
||||||
<string name="error_media_upload_size">파일은 8MB보다 작아야 합니다.</string>
|
<string name="error_image_upload_size">파일은 8MB보다 작아야 합니다.</string>
|
||||||
<string name="error_media_upload_type">이 형태의 파일은 업로드될 수 없습니다.</string>
|
<string name="error_media_upload_type">이 형태의 파일은 업로드될 수 없습니다.</string>
|
||||||
<string name="error_media_upload_opening">그 파일은 열 수 없습니다.</string>
|
<string name="error_media_upload_opening">그 파일은 열 수 없습니다.</string>
|
||||||
<string name="error_media_upload_permission">미디어를 읽기 위한 권한이 필요합니다.</string>
|
<string name="error_media_upload_permission">미디어를 읽기 위한 권한이 필요합니다.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Autorisatie werd geweigerd.</string>
|
<string name="error_authorization_denied">Autorisatie werd geweigerd.</string>
|
||||||
<string name="error_retrieving_oauth_token">Kon geen inlogsleutel verkrijgen.</string>
|
<string name="error_retrieving_oauth_token">Kon geen inlogsleutel verkrijgen.</string>
|
||||||
<string name="error_compose_character_limit">Tekst van deze toot is te lang!</string>
|
<string name="error_compose_character_limit">Tekst van deze toot is te lang!</string>
|
||||||
<string name="error_media_upload_size">Bestand moet kleiner zijn dan 8MB.</string>
|
<string name="error_image_upload_size">Bestand moet kleiner zijn dan 8MB.</string>
|
||||||
<string name="error_media_upload_type">Bestandstype kan niet worden geüpload.</string>
|
<string name="error_media_upload_type">Bestandstype kan niet worden geüpload.</string>
|
||||||
<string name="error_media_upload_opening">Bestand kon niet worden geopend.</string>
|
<string name="error_media_upload_opening">Bestand kon niet worden geopend.</string>
|
||||||
<string name="error_media_upload_permission">Er is toestemming nodig om deze media te lezen.</string>
|
<string name="error_media_upload_permission">Er is toestemming nodig om deze media te lezen.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">L\'autoritzacion es estada regetada.</string>
|
<string name="error_authorization_denied">L\'autoritzacion es estada regetada.</string>
|
||||||
<string name="error_retrieving_oauth_token">Fracàs de l’obtencion del testimoni d\'iniciacion de session.</string>
|
<string name="error_retrieving_oauth_token">Fracàs de l’obtencion del testimoni d\'iniciacion de session.</string>
|
||||||
<string name="error_compose_character_limit">L\'estatut es tròp long !</string>
|
<string name="error_compose_character_limit">L\'estatut es tròp long !</string>
|
||||||
<string name="error_media_upload_size">Lo fichièr a d’èsser inferior a 8Mo.</string>
|
<string name="error_image_upload_size">Lo fichièr a d’èsser inferior a 8Mo.</string>
|
||||||
<string name="error_media_upload_type">Aqueste tip de fichièr se pòt pas mandar.</string>
|
<string name="error_media_upload_type">Aqueste tip de fichièr se pòt pas mandar.</string>
|
||||||
<string name="error_media_upload_opening">Aqueste tip de fichièr se pòt pas dobrir.</string>
|
<string name="error_media_upload_opening">Aqueste tip de fichièr se pòt pas dobrir.</string>
|
||||||
<string name="error_media_upload_permission">Cal permís de lectura del mèdia.</string>
|
<string name="error_media_upload_permission">Cal permís de lectura del mèdia.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Odmówiono autoryzacji.</string>
|
<string name="error_authorization_denied">Odmówiono autoryzacji.</string>
|
||||||
<string name="error_retrieving_oauth_token">Nie udało się uzyskać tokenu logowania.</string>
|
<string name="error_retrieving_oauth_token">Nie udało się uzyskać tokenu logowania.</string>
|
||||||
<string name="error_compose_character_limit">Zbyt długi wpis!</string>
|
<string name="error_compose_character_limit">Zbyt długi wpis!</string>
|
||||||
<string name="error_media_upload_size">Plik może mieć maksymalnie 8 MB.</string>
|
<string name="error_image_upload_size">Plik może mieć maksymalnie 8 MB.</string>
|
||||||
<string name="error_media_upload_type">Ten format pliku nie może zostać wysłany.</string>
|
<string name="error_media_upload_type">Ten format pliku nie może zostać wysłany.</string>
|
||||||
<string name="error_media_upload_opening">Nie można otworzyć tego pliku.</string>
|
<string name="error_media_upload_opening">Nie można otworzyć tego pliku.</string>
|
||||||
<string name="error_media_upload_permission">Wymagane jest pozwolenie na dostęp do plików z urządzenia.</string>
|
<string name="error_media_upload_permission">Wymagane jest pozwolenie na dostęp do plików z urządzenia.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Autorização negada.</string>
|
<string name="error_authorization_denied">Autorização negada.</string>
|
||||||
<string name="error_retrieving_oauth_token">Falha ao adquirir token de entrada.</string>
|
<string name="error_retrieving_oauth_token">Falha ao adquirir token de entrada.</string>
|
||||||
<string name="error_compose_character_limit">A postagem é muito longa!</string>
|
<string name="error_compose_character_limit">A postagem é muito longa!</string>
|
||||||
<string name="error_media_upload_size">O arquivo deve ser menor que 8MB.</string>
|
<string name="error_image_upload_size">O arquivo deve ser menor que 8MB.</string>
|
||||||
<string name="error_media_upload_type">Esse tipo de arquivo não pode ser enviado.</string>
|
<string name="error_media_upload_type">Esse tipo de arquivo não pode ser enviado.</string>
|
||||||
<string name="error_media_upload_opening">Esse arquvo não pode ser aberto.</string>
|
<string name="error_media_upload_opening">Esse arquvo não pode ser aberto.</string>
|
||||||
<string name="error_media_upload_permission">Permissão para ler mídia é necessária.</string>
|
<string name="error_media_upload_permission">Permissão para ler mídia é necessária.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Авторизация была отклонена.</string>
|
<string name="error_authorization_denied">Авторизация была отклонена.</string>
|
||||||
<string name="error_retrieving_oauth_token">Не удалось получить токен авторизации.</string>
|
<string name="error_retrieving_oauth_token">Не удалось получить токен авторизации.</string>
|
||||||
<string name="error_compose_character_limit">Статус слишком длинный!</string>
|
<string name="error_compose_character_limit">Статус слишком длинный!</string>
|
||||||
<string name="error_media_upload_size">Файл должен быть не больше 8 Мбайт.</string>
|
<string name="error_image_upload_size">Файл должен быть не больше 8 Мбайт.</string>
|
||||||
<string name="error_media_upload_type">Данный тип файла не может быть загружен.</string>
|
<string name="error_media_upload_type">Данный тип файла не может быть загружен.</string>
|
||||||
<string name="error_media_upload_opening">Файл не может быть открыт.</string>
|
<string name="error_media_upload_opening">Файл не может быть открыт.</string>
|
||||||
<string name="error_media_upload_permission">Необходимо разрешение на чтение медиаконтента.</string>
|
<string name="error_media_upload_permission">Необходимо разрешение на чтение медиаконтента.</string>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<string name="error_authorization_denied">Ingen behörighet.</string>
|
<string name="error_authorization_denied">Ingen behörighet.</string>
|
||||||
<string name="error_retrieving_oauth_token">Misslyckades med att få en inloggnings-token.</string>
|
<string name="error_retrieving_oauth_token">Misslyckades med att få en inloggnings-token.</string>
|
||||||
<string name="error_compose_character_limit">Statusen är för lång!</string>
|
<string name="error_compose_character_limit">Statusen är för lång!</string>
|
||||||
<string name="error_media_upload_size">Filen måste vara mindre än 8MB.</string>
|
<string name="error_image_upload_size">Filen måste vara mindre än 8MB.</string>
|
||||||
<string name="error_media_upload_type">Den typen av fil kan inte laddas upp.</string>
|
<string name="error_media_upload_type">Den typen av fil kan inte laddas upp.</string>
|
||||||
<string name="error_media_upload_opening">Den filen kunde inte öppnas.</string>
|
<string name="error_media_upload_opening">Den filen kunde inte öppnas.</string>
|
||||||
<string name="error_media_upload_permission">Tillstånd att läsa media krävs.</string>
|
<string name="error_media_upload_permission">Tillstånd att läsa media krävs.</string>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<string name="error_authorization_denied">அங்கீகாரம் மறுக்கப்பட்டுள்ளது</string>
|
<string name="error_authorization_denied">அங்கீகாரம் மறுக்கப்பட்டுள்ளது</string>
|
||||||
<string name="error_retrieving_oauth_token">உள்நுழைவு டோக்கனைப் பெறுவதில் தோல்வி.</string>
|
<string name="error_retrieving_oauth_token">உள்நுழைவு டோக்கனைப் பெறுவதில் தோல்வி.</string>
|
||||||
<string name="error_compose_character_limit">நிலை மிக நீளமாக உள்ளது!</string>
|
<string name="error_compose_character_limit">நிலை மிக நீளமாக உள்ளது!</string>
|
||||||
<string name="error_media_upload_size">கோப்பு 4MB-க்கும் குறைவாக இருக்க வேண்டும்.</string>
|
<string name="error_image_upload_size">கோப்பு 4MB-க்கும் குறைவாக இருக்க வேண்டும்.</string>
|
||||||
<string name="error_media_upload_type">இந்த வகை கோப்பை பதிவேற்ற முடியாது.</string>
|
<string name="error_media_upload_type">இந்த வகை கோப்பை பதிவேற்ற முடியாது.</string>
|
||||||
<string name="error_media_upload_opening">அந்த கோப்பை திறக்க முடியவில்லை.</string>
|
<string name="error_media_upload_opening">அந்த கோப்பை திறக்க முடியவில்லை.</string>
|
||||||
<string name="error_media_upload_permission">ஊடகத்தை படிக்க அனுமதி தேவை.</string>
|
<string name="error_media_upload_permission">ஊடகத்தை படிக்க அனுமதி தேவை.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">Kimlik doğrulama reddedildi.</string>
|
<string name="error_authorization_denied">Kimlik doğrulama reddedildi.</string>
|
||||||
<string name="error_retrieving_oauth_token">Giriş jetonu alınamadı.</string>
|
<string name="error_retrieving_oauth_token">Giriş jetonu alınamadı.</string>
|
||||||
<string name="error_compose_character_limit">İleti fazlasıyla uzun!</string>
|
<string name="error_compose_character_limit">İleti fazlasıyla uzun!</string>
|
||||||
<string name="error_media_upload_size">Dosya 8MB\'ten küçük olmalı.</string>
|
<string name="error_image_upload_size">Dosya 8MB\'ten küçük olmalı.</string>
|
||||||
<string name="error_media_upload_type">O biçim dosya yüklenmez.</string>
|
<string name="error_media_upload_type">O biçim dosya yüklenmez.</string>
|
||||||
<string name="error_media_upload_opening">O dosya açılamadı.</string>
|
<string name="error_media_upload_opening">O dosya açılamadı.</string>
|
||||||
<string name="error_media_upload_permission">Medya okuma izni gerekiyor.</string>
|
<string name="error_media_upload_permission">Medya okuma izni gerekiyor.</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">授权被拒绝。</string>
|
<string name="error_authorization_denied">授权被拒绝。</string>
|
||||||
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
||||||
<string name="error_compose_character_limit">嘟文太长了!</string>
|
<string name="error_compose_character_limit">嘟文太长了!</string>
|
||||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
<string name="error_image_upload_size">文件大小限制 8MB。</string>
|
||||||
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
||||||
<string name="error_media_upload_opening">此文件无法打开。</string>
|
<string name="error_media_upload_opening">此文件无法打开。</string>
|
||||||
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">授權被拒絕。</string>
|
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
<string name="error_image_upload_size">文件大小限制 8MB。</string>
|
||||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">授權被拒絕。</string>
|
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
<string name="error_image_upload_size">文件大小限制 8MB。</string>
|
||||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">授权被拒绝。</string>
|
<string name="error_authorization_denied">授权被拒绝。</string>
|
||||||
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
||||||
<string name="error_compose_character_limit">嘟文太长了!</string>
|
<string name="error_compose_character_limit">嘟文太长了!</string>
|
||||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
<string name="error_image_upload_size">文件大小限制 8MB。</string>
|
||||||
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
||||||
<string name="error_media_upload_opening">此文件无法打开。</string>
|
<string name="error_media_upload_opening">此文件无法打开。</string>
|
||||||
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<string name="error_authorization_denied">授權被拒絕。</string>
|
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
<string name="error_image_upload_size">文件大小限制 8MB。</string>
|
||||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
<string name="error_authorization_denied">Authorization was denied.</string>
|
<string name="error_authorization_denied">Authorization was denied.</string>
|
||||||
<string name="error_retrieving_oauth_token">Failed getting a login token.</string>
|
<string name="error_retrieving_oauth_token">Failed getting a login token.</string>
|
||||||
<string name="error_compose_character_limit">The status is too long!</string>
|
<string name="error_compose_character_limit">The status is too long!</string>
|
||||||
<string name="error_media_upload_size">The file must be less than 8MB.</string>
|
<string name="error_image_upload_size">The file must be less than 8MB.</string>
|
||||||
|
<string name="error_video_upload_size">Video files must be less than 40MB.</string>
|
||||||
<string name="error_media_upload_type">That type of file cannot be uploaded.</string>
|
<string name="error_media_upload_type">That type of file cannot be uploaded.</string>
|
||||||
<string name="error_media_upload_opening">That file could not be opened.</string>
|
<string name="error_media_upload_opening">That file could not be opened.</string>
|
||||||
<string name="error_media_upload_permission">Permission to read media is required.</string>
|
<string name="error_media_upload_permission">Permission to read media is required.</string>
|
||||||
@ -354,4 +355,6 @@
|
|||||||
<string name="profile_metadata_label_label">Label</string>
|
<string name="profile_metadata_label_label">Label</string>
|
||||||
<string name="profile_metadata_content_label">Content</string>
|
<string name="profile_metadata_content_label">Content</string>
|
||||||
|
|
||||||
|
<string name="pref_title_absolute_time">Use absolute time</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -48,6 +48,12 @@
|
|||||||
android:title="@string/pref_appearance_long_posts_title"
|
android:title="@string/pref_appearance_long_posts_title"
|
||||||
android:summaryOn="@string/pref_appearance_long_posts_enabled"
|
android:summaryOn="@string/pref_appearance_long_posts_enabled"
|
||||||
android:summaryOff="@string/pref_appearance_long_posts_disabled"/>
|
android:summaryOff="@string/pref_appearance_long_posts_disabled"/>
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="absoluteTimeView"
|
||||||
|
android:title="@string/pref_title_absolute_time" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_publishing">
|
<PreferenceCategory android:title="@string/pref_publishing">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user