Merge remote-tracking branch 'tuskyapp/master'

This commit is contained in:
kyori19 2019-09-07 17:55:52 +09:00
commit c11ea7ae80
15 changed files with 63 additions and 68 deletions

View File

@ -115,7 +115,7 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0-alpha05' implementation 'com.google.android.material:material:1.1.0-alpha05'
implementation 'androidx.exifinterface:exifinterface:1.0.0' implementation 'androidx.exifinterface:exifinterface:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference:1.1.0-rc01' implementation 'androidx.preference:preference:1.1.0-alpha04'
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"

View File

@ -1248,6 +1248,10 @@ public final class ComposeActivity
} }
private void readyStatus(final Status.Visibility visibility, final boolean sensitive) { private void readyStatus(final Status.Visibility visibility, final boolean sensitive) {
if (waitForMediaLatch.isEmpty()) {
onReadySuccess(visibility, sensitive);
return;
}
finishingUploadDialog = ProgressDialog.show( finishingUploadDialog = ProgressDialog.show(
this, getString(R.string.dialog_title_finishing_media_upload), this, getString(R.string.dialog_title_finishing_media_upload),
getString(R.string.dialog_message_uploading_media), true, true); getString(R.string.dialog_message_uploading_media), true, true);

View File

@ -422,8 +422,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
} }
} }
private void loadImage(MediaPreviewImageView imageView, String previewUrl, String description, private void loadImage(MediaPreviewImageView imageView, String previewUrl, MetaData meta) {
MetaData meta) {
if (TextUtils.isEmpty(previewUrl)) { if (TextUtils.isEmpty(previewUrl)) {
Glide.with(imageView) Glide.with(imageView)
.load(mediaPreviewUnloadedId) .load(mediaPreviewUnloadedId)
@ -473,7 +472,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
} }
if (!sensitive || showingContent) { if (!sensitive || showingContent) {
loadImage(imageView, previewUrl, description, attachments.get(i).getMeta()); loadImage(imageView, previewUrl, attachments.get(i).getMeta());
} else { } else {
imageView.setImageResource(mediaPreviewUnloadedId); imageView.setImageResource(mediaPreviewUnloadedId);
} }
@ -546,6 +545,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
case GIFV: case GIFV:
case VIDEO: case VIDEO:
return R.drawable.ic_videocam_24dp; return R.drawable.ic_videocam_24dp;
case AUDIO:
return R.drawable.ic_music_box_24dp;
} }
} }
@ -593,11 +594,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
} }
private static CharSequence getAttachmentDescription(Context context, Attachment attachment) { private static CharSequence getAttachmentDescription(Context context, Attachment attachment) {
String duration = "";
if(attachment.getMeta().getDuration() != null && attachment.getMeta().getDuration() > 0) {
duration = formatDuration(attachment.getMeta().getDuration()) + " ";
}
if (TextUtils.isEmpty(attachment.getDescription())) { if (TextUtils.isEmpty(attachment.getDescription())) {
return context return duration + context.getString(R.string.description_status_media_no_description_placeholder);
.getString(R.string.description_status_media_no_description_placeholder);
} else { } else {
return attachment.getDescription(); return duration + attachment.getDescription();
} }
} }
@ -728,7 +732,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
setQuoteContainer(status.getQuote(), listener); setQuoteContainer(status.getQuote(), listener);
List<Attachment> attachments = status.getAttachments(); List<Attachment> attachments = status.getAttachments();
boolean sensitive = status.isSensitive(); boolean sensitive = status.isSensitive();
if (mediaPreviewEnabled) { if (mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
setMediaPreviews(attachments, sensitive, listener, status.isShowingContent()); setMediaPreviews(attachments, sensitive, listener, status.isShowingContent());
if (attachments.size() == 0) { if (attachments.size() == 0) {
@ -776,6 +780,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
} }
} }
protected static boolean hasAudioAttachment(List<Attachment> attachments) {
for(Attachment attachment: attachments) {
if (attachment.getType() == Attachment.Type.AUDIO) {
return true;
}
}
return false;
}
private void setDescriptionForStatus(@NonNull StatusViewData.Concrete status) { private void setDescriptionForStatus(@NonNull StatusViewData.Concrete status) {
Context context = itemView.getContext(); Context context = itemView.getContext();
@ -976,4 +989,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo); return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
} }
private static String formatDuration(double durationInSeconds) {
int seconds = (int) Math.round(durationInSeconds) % 60;
int minutes = (int) durationInSeconds % 3600 / 60;
int hours = (int) durationInSeconds / 3600;
return String.format("%d:%02d:%02d", hours, minutes, seconds);
}
} }

View File

@ -85,7 +85,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
setFavourited(status.getFavourited()); setFavourited(status.getFavourited());
List<Attachment> attachments = status.getAttachments(); List<Attachment> attachments = status.getAttachments();
boolean sensitive = status.getSensitive(); boolean sensitive = status.getSensitive();
if(mediaPreviewEnabled) { if(mediaPreviewEnabled && !hasAudioAttachment(attachments)) {
setMediaPreviews(attachments, sensitive, listener, status.getShowingHiddenContent()); setMediaPreviews(attachments, sensitive, listener, status.getShowingHiddenContent());
if (attachments.size() == 0) { if (attachments.size() == 0) {

View File

@ -42,6 +42,8 @@ data class Attachment(
GIFV, GIFV,
@SerializedName("video") @SerializedName("video")
VIDEO, VIDEO,
@SerializedName("audio")
AUDIO,
@SerializedName("unknown") @SerializedName("unknown")
UNKNOWN UNKNOWN
} }
@ -53,6 +55,7 @@ data class Attachment(
"\"image\"" -> Type.IMAGE "\"image\"" -> Type.IMAGE
"\"gifv\"" -> Type.GIFV "\"gifv\"" -> Type.GIFV
"\"video\"" -> Type.VIDEO "\"video\"" -> Type.VIDEO
"\"audio\"" -> Type.AUDIO
else -> Type.UNKNOWN else -> Type.UNKNOWN
} }
} }
@ -63,7 +66,8 @@ data class Attachment(
*/ */
@Parcelize @Parcelize
data class MetaData ( data class MetaData (
val focus: Focus? val focus: Focus?,
val duration: Float?
) : Parcelable ) : Parcelable
/** /**

View File

@ -29,6 +29,6 @@ data class DeletedStatus(
@SerializedName("created_at") val createdAt: Date @SerializedName("created_at") val createdAt: Date
) { ) {
fun isEmpty(): Boolean { fun isEmpty(): Boolean {
return text == null && attachments == null; return text == null && attachments == null
} }
} }

View File

@ -1,14 +0,0 @@
package com.keylesspalace.tusky.entity
import com.google.gson.annotations.SerializedName
data class History(
@field:SerializedName("day")
val day: String,
@field:SerializedName("uses")
val uses: Int,
@field:SerializedName("accounts")
val accounts: Int
)

View File

@ -261,6 +261,7 @@ public class NotificationsFragment extends SFragment implements
buttonFilter.setOnClickListener(v -> showFilterMenu()); buttonFilter.setOnClickListener(v -> showFilterMenu());
if (notifications.isEmpty()) { if (notifications.isEmpty()) {
swipeRefreshLayout.setEnabled(false);
sendFetchNotificationsRequest(null, null, FetchEnd.BOTTOM, -1); sendFetchNotificationsRequest(null, null, FetchEnd.BOTTOM, -1);
} else { } else {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
@ -375,7 +376,6 @@ public class NotificationsFragment extends SFragment implements
@Override @Override
public void onRefresh() { public void onRefresh() {
swipeRefreshLayout.setEnabled(true);
this.statusView.setVisibility(View.GONE); this.statusView.setVisibility(View.GONE);
Either<Placeholder, Notification> first = CollectionsKt.firstOrNull(this.notifications); Either<Placeholder, Notification> first = CollectionsKt.firstOrNull(this.notifications);
String topId; String topId;
@ -946,7 +946,8 @@ public class NotificationsFragment extends SFragment implements
if (notifications.size() == 0 && adapter.getItemCount() == 0) { if (notifications.size() == 0 && adapter.getItemCount() == 0) {
this.statusView.setVisibility(View.VISIBLE); this.statusView.setVisibility(View.VISIBLE);
this.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null); this.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
} else {
swipeRefreshLayout.setEnabled(true);
} }
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);

View File

@ -339,7 +339,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
switch (type) { switch (type) {
case GIFV: case GIFV:
case VIDEO: case VIDEO:
case IMAGE: { case IMAGE:
case AUDIO: {
final List<AttachmentViewData> attachments = AttachmentViewData.list(actionable); final List<AttachmentViewData> attachments = AttachmentViewData.list(actionable);
final Intent intent = ViewMediaActivity.newIntent(getContext(), attachments, final Intent intent = ViewMediaActivity.newIntent(getContext(), attachments,
urlIndex); urlIndex);

View File

@ -51,7 +51,8 @@ abstract class ViewMediaFragment : BaseFragment(), SharedElementTransitionListen
val fragment = when (attachment.type) { val fragment = when (attachment.type) {
Attachment.Type.IMAGE -> ViewImageFragment() Attachment.Type.IMAGE -> ViewImageFragment()
Attachment.Type.VIDEO, Attachment.Type.VIDEO,
Attachment.Type.GIFV -> ViewVideoFragment() Attachment.Type.GIFV,
Attachment.Type.AUDIO -> ViewVideoFragment()
else -> ViewImageFragment() // it probably won't show anything, but its better than crashing else -> ViewImageFragment() // it probably won't show anything, but its better than crashing
} }
fragment.arguments = arguments fragment.arguments = arguments

View File

@ -44,4 +44,8 @@ public class CountUpDownLatch {
wait(); wait();
} }
} }
public synchronized boolean isEmpty() {
return count == 0;
}
} }

View File

@ -1,35 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.keylesspalace.tusky.util
import androidx.lifecycle.LiveData
import androidx.paging.PagedList
/**
* Data class that is necessary for a UI to show a listing and interact w/ the rest of the system
*/
data class MultiListing(
val pagedLists: List<LiveData<PagedList<Any>>>,
// represents the network request status to show to the user
val networkState: LiveData<NetworkState>,
// represents the refresh status to show to the user. Separate from networkState, this
// value is importantly only when refresh is requested.
val refreshState: LiveData<NetworkState>,
// refreshes the whole data and fetches it from scratch.
val refresh: () -> Unit,
// retries any failed requests.
val retry: () -> Unit)

View File

@ -34,7 +34,7 @@ fun deserialize(data: String?): Set<Notification.Type> {
val ret = HashSet<Notification.Type>() val ret = HashSet<Notification.Type>()
data?.let { data?.let {
val array = JSONArray(data) val array = JSONArray(data)
for (i in 0..(array.length() - 1)) { for (i in 0 until array.length()) {
val item = array.getString(i) val item = array.getString(i)
val type = Notification.Type.byString(item) val type = Notification.Type.byString(item)
if (type != Notification.Type.UNKNOWN) if (type != Notification.Type.UNKNOWN)

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M16,9H13V14.5A2.5,2.5 0 0,1 10.5,17A2.5,2.5 0 0,1 8,14.5A2.5,2.5 0 0,1 10.5,12C11.07,12 11.58,12.19 12,12.5V7H16M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3Z" />
</vector>

View File

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.41' ext.kotlin_version = '1.3.50'
repositories { repositories {
jcenter() jcenter()
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta05' classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta06'
classpath 'com.android.tools.build:gradle:3.4.2' classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }