Fix lint warnings (#4019)

Clears the baseline of issues in our code, and resolves most of the
straightforward warnings from the report
This commit is contained in:
Levi Bard 2023-09-13 09:20:53 +02:00 committed by GitHub
parent 7dfc8790c7
commit f99cb6d1d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 154 additions and 252 deletions

View File

@ -1,17 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.1.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.1)" variant="all" version="8.1.1">
<issue
id="MissingPermission"
message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
errorLine1=" notificationManager.notify(notificationId, builder.build())"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt"
line="78"
column="17"/>
</issue>
<issue
id="CheckResult"
message="The result of `placeholder` is not used"
@ -72,8 +61,6 @@
<location
file="$GRADLE_USER_HOME/caches/modules-2/files-2.1/org.pageseeder.diffx/pso-diffx/1.1.1/b655ebc87588a857a4f3d88cf98bcefa87a6105b/pso-diffx-1.1.1.jar"/>
</issue>
<issue
id="UseAppTint"
message="Must use `app:tint` instead of `android:tint`"
errorLine1=" android:tint=&quot;?android:attr/textColorTertiary&quot;"
@ -84,72 +71,6 @@
column="13"/>
</issue>
<issue
id="UseCompatLoadingForDrawables"
message="Use `AppCompatResources.getDrawable()`"
errorLine1=" return ctx.getDrawable(R.drawable.avatar_default)!!"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/MainActivity.kt"
line="533"
column="28"/>
</issue>
<issue
id="UseCompatTextViewDrawableXml"
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
errorLine1=" android:drawableStart=&quot;@drawable/ic_briefcase&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/activity_account.xml"
line="282"
column="29"/>
</issue>
<issue
id="UseCompatTextViewDrawableXml"
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
errorLine1=" android:drawableStart=&quot;@drawable/ic_plus_24dp&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/activity_tab_preference.xml"
line="69"
column="17"/>
</issue>
<issue
id="UseCompatTextViewDrawableXml"
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
errorLine1=" android:drawableStart=&quot;@drawable/ic_person_add_24dp&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_follow.xml"
line="17"
column="9"/>
</issue>
<issue
id="UseCompatTextViewDrawableXml"
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
errorLine1=" android:drawableStart=&quot;@drawable/ic_reblog_18dp&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/item_status.xml"
line="20"
column="9"/>
</issue>
<issue
id="UseCompatTextViewDrawableXml"
message="Use `app:drawableStartCompat` instead of `android:drawableStart`"
errorLine1=" android:drawableStart=&quot;@drawable/ic_poll_24dp&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/view_poll_preview.xml"
line="15"
column="9"/>
</issue>
<issue
id="PluralsCandidate"
message="Formatting %d followed by words (&quot;posts&quot;): This should probably be a plural rather than a string"
@ -436,28 +357,6 @@
column="13"/>
</issue>
<issue
id="Recycle"
message="This `InputStream` should be freed up after use with `#close()`"
errorLine1=" from = contentResolver.openInputStream(this)"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/util/IOUtils.kt"
line="45"
column="32"/>
</issue>
<issue
id="Recycle"
message="This `InputStream` should be freed up after use with `#close()`"
errorLine1=" val stream = contentResolver.openInputStream(media.uri)"
errorLine2=" ~~~~~~~~~~~~~~~">
<location
file="src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt"
line="274"
column="42"/>
</issue>
<issue
id="ObsoleteSdkInt"
message="Unnecessary; SDK_INT is always >= 24"
@ -734,15 +633,6 @@
</issue>
<issue
id="KeyboardInaccessibleWidget"
message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
errorLine1=" android:clickable=&quot;true&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/res/layout/material_drawer_header.xml"
line="8"
column="5"/>
</issue>
<issue
id="SetTextI18n"

View File

@ -62,6 +62,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
private static final String TAG = "BaseActivity";
@Inject
@NonNull
public AccountManager accountManager;
private static final int REQUESTER_NONE = Integer.MAX_VALUE;
@ -164,13 +165,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
return style;
}
public void startActivityWithSlideInAnimation(Intent intent) {
public void startActivityWithSlideInAnimation(@NonNull Intent intent) {
super.startActivity(intent);
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed();
return true;
@ -198,7 +199,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
}
}
protected void showErrorDialog(View anyView, @StringRes int descriptionId, @StringRes int actionId, View.OnClickListener listener) {
protected void showErrorDialog(@Nullable View anyView, @StringRes int descriptionId, @StringRes int actionId, @Nullable View.OnClickListener listener) {
if (anyView != null) {
Snackbar bar = Snackbar.make(anyView, getString(descriptionId), Snackbar.LENGTH_SHORT);
bar.setAction(actionId, listener);
@ -206,7 +207,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
}
}
public void showAccountChooserDialog(CharSequence dialogTitle, boolean showActiveAccount, AccountSelectionListener listener) {
public void showAccountChooserDialog(@Nullable CharSequence dialogTitle, boolean showActiveAccount, @NonNull AccountSelectionListener listener) {
List<AccountEntity> accounts = accountManager.getAllAccountsOrderedByActive();
AccountEntity activeAccount = accountManager.getActiveAccount();
@ -273,7 +274,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
}
}
public void requestPermissions(String[] permissions, PermissionRequester requester) {
public void requestPermissions(@NonNull String[] permissions, @NonNull PermissionRequester requester) {
ArrayList<String> permissionsToRequest = new ArrayList<>();
for(String permission: permissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {

View File

@ -28,6 +28,7 @@ import android.graphics.drawable.Animatable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
@ -180,6 +181,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
/** Adapter for the different timeline tabs */
private lateinit var tabAdapter: MainPagerAdapter
@Suppress("DEPRECATION")
@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -355,7 +357,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (
Build.VERSION.SDK_INT >= 33 &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
@ -530,7 +535,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun placeholder(ctx: Context, tag: String?): Drawable {
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) {
return ctx.getDrawable(R.drawable.avatar_default)!!
return AppCompatResources.getDrawable(ctx, R.drawable.avatar_default)!!
}
return super.placeholder(ctx, tag)
@ -914,6 +919,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
updateShortcut(this, accountManager.activeAccount!!)
}
@SuppressLint("CheckResult")
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
val animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)

View File

@ -130,7 +130,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private final Drawable mediaPreviewUnloaded;
protected StatusBaseViewHolder(View itemView) {
protected StatusBaseViewHolder(@NonNull View itemView) {
super(itemView);
displayName = itemView.findViewById(R.id.status_display_name);
username = itemView.findViewById(R.id.status_username);
@ -191,14 +191,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
TouchDelegateHelper.expandTouchSizeToFillRow((ViewGroup) itemView, CollectionsKt.listOfNotNull(replyButton, reblogButton, favouriteButton, bookmarkButton, moreButton));
}
protected void setDisplayName(String name, List<Emoji> customEmojis, StatusDisplayOptions statusDisplayOptions) {
protected void setDisplayName(@NonNull String name, @Nullable List<Emoji> customEmojis, @NonNull StatusDisplayOptions statusDisplayOptions) {
CharSequence emojifiedName = CustomEmojiHelper.emojify(
name, customEmojis, displayName, statusDisplayOptions.animateEmojis()
);
displayName.setText(emojifiedName);
}
protected void setUsername(String name) {
protected void setUsername(@Nullable String name) {
Context context = username.getContext();
String usernameText = context.getString(R.string.post_username_format, name);
username.setText(usernameText);
@ -210,7 +210,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
protected void setSpoilerAndContent(@NonNull StatusViewData.Concrete status,
@NonNull StatusDisplayOptions statusDisplayOptions,
final StatusActionListener listener) {
final @NonNull StatusActionListener listener) {
Status actionable = status.getActionable();
String spoilerText = actionable.getSpoilerText();
@ -340,7 +340,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
Collections.singletonList(new CompositeWithOpaqueBackground(avatar)));
}
protected void setMetaData(StatusViewData.Concrete statusViewData, StatusDisplayOptions statusDisplayOptions, StatusActionListener listener) {
protected void setMetaData(@NonNull StatusViewData.Concrete statusViewData, @NonNull StatusDisplayOptions statusDisplayOptions, @NonNull StatusActionListener listener) {
Status status = statusViewData.getActionable();
Date createdAt = status.getCreatedAt();
@ -491,9 +491,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
protected void setMediaPreviews(
final List<Attachment> attachments,
final @NonNull List<Attachment> attachments,
boolean sensitive,
final StatusActionListener listener,
final @NonNull StatusActionListener listener,
boolean showingContent,
boolean useBlurhash
) {
@ -584,8 +584,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
mediaLabels[index].setText(label);
}
protected void setMediaLabel(List<Attachment> attachments, boolean sensitive,
final StatusActionListener listener, boolean showingContent) {
protected void setMediaLabel(@NonNull List<Attachment> attachments, boolean sensitive,
final @NonNull StatusActionListener listener, boolean showingContent) {
Context context = itemView.getContext();
for (int i = 0; i < mediaLabels.length; i++) {
TextView mediaLabel = mediaLabels[i];
@ -606,7 +606,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
private void setAttachmentClickListener(View view, StatusActionListener listener,
private void setAttachmentClickListener(View view, @NonNull StatusActionListener listener,
int index, Attachment attachment, boolean animateTransition) {
view.setOnClickListener(v -> {
int position = getBindingAdapterPosition();
@ -630,10 +630,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
sensitiveMediaShow.setVisibility(View.GONE);
}
protected void setupButtons(final StatusActionListener listener,
final String accountId,
final String statusContent,
StatusDisplayOptions statusDisplayOptions) {
protected void setupButtons(final @NonNull StatusActionListener listener,
final @NonNull String accountId,
final @Nullable String statusContent,
@NonNull StatusDisplayOptions statusDisplayOptions) {
View.OnClickListener profileButtonClickListener = button -> listener.onViewAccount(accountId);
avatar.setOnClickListener(profileButtonClickListener);
@ -752,8 +752,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
popup.show();
}
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
StatusDisplayOptions statusDisplayOptions) {
public void setupWithStatus(@NonNull StatusViewData.Concrete status, final @NonNull StatusActionListener listener,
@NonNull StatusDisplayOptions statusDisplayOptions) {
this.setupWithStatus(status, listener, statusDisplayOptions, null);
}
@ -843,7 +843,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
filteredPlaceholderShowButton.setOnClickListener(view -> listener.clearWarningAction(getBindingAdapterPosition()));
}
protected static boolean hasPreviewableAttachment(List<Attachment> attachments) {
protected static boolean hasPreviewableAttachment(@NonNull List<Attachment> attachments) {
for (Attachment attachment : attachments) {
if (attachment.getType() == Attachment.Type.AUDIO || attachment.getType() == Attachment.Type.UNKNOWN) {
return false;
@ -918,7 +918,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
protected static CharSequence getVisibilityDescription(Context context, Status.Visibility visibility) {
@NonNull
protected static CharSequence getVisibilityDescription(@NonNull Context context, @Nullable Status.Visibility visibility) {
if (visibility == null) {
return "";
@ -967,7 +968,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
protected CharSequence getFavsText(Context context, int count) {
@NonNull
protected CharSequence getFavsText(@NonNull Context context, int count) {
if (count > 0) {
String countString = numberFormat.format(count);
return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.favs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
@ -976,7 +978,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
}
protected CharSequence getReblogsText(Context context, int count) {
@NonNull
protected CharSequence getReblogsText(@NonNull Context context, int count) {
if (count > 0) {
String countString = numberFormat.format(count);
return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.reblogs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
@ -1077,11 +1080,11 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
protected void setupCard(
final StatusViewData.Concrete status,
final @NonNull StatusViewData.Concrete status,
boolean expanded,
final CardViewMode cardViewMode,
final StatusDisplayOptions statusDisplayOptions,
final StatusActionListener listener
final @NonNull CardViewMode cardViewMode,
final @NonNull StatusDisplayOptions statusDisplayOptions,
final @NonNull StatusActionListener listener
) {
if (cardView == null) {
return;

View File

@ -35,7 +35,7 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
private static final DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.SHORT);
public StatusDetailedViewHolder(View view) {
public StatusDetailedViewHolder(@NonNull View view) {
super(view);
reblogs = view.findViewById(R.id.status_reblogs);
favourites = view.findViewById(R.id.status_favourites);
@ -43,7 +43,7 @@ public class StatusDetailedViewHolder extends StatusBaseViewHolder {
}
@Override
protected void setMetaData(StatusViewData.Concrete statusViewData, StatusDisplayOptions statusDisplayOptions, StatusActionListener listener) {
protected void setMetaData(@NonNull StatusViewData.Concrete statusViewData, @NonNull StatusDisplayOptions statusDisplayOptions, @NonNull StatusActionListener listener) {
Status status = statusViewData.getActionable();

View File

@ -51,7 +51,7 @@ public class StatusViewHolder extends StatusBaseViewHolder {
private final TextView favouritedCountLabel;
private final TextView reblogsCountLabel;
public StatusViewHolder(View itemView) {
public StatusViewHolder(@NonNull View itemView) {
super(itemView);
statusInfo = itemView.findViewById(R.id.status_info);
contentCollapseButton = itemView.findViewById(R.id.button_toggle_content);

View File

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.components.announcements
import android.annotation.SuppressLint
import android.os.Build
import android.text.SpannableStringBuilder
import android.view.ContextThemeWrapper
@ -55,6 +56,7 @@ class AnnouncementAdapter(
return BindingHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: BindingHolder<ItemAnnouncementBinding>, position: Int) {
val item = items[position]

View File

@ -275,7 +275,7 @@ class ComposeViewModel @Inject constructor(
val mediaUris: MutableList<String> = mutableListOf()
val mediaDescriptions: MutableList<String?> = mutableListOf()
val mediaFocus: MutableList<Attachment.Focus?> = mutableListOf()
media.value.forEach { item ->
for (item in media.value) {
mediaUris.add(item.uri.toString())
mediaDescriptions.add(item.description)
mediaFocus.add(item.focus)

View File

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.components.compose
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.media.MediaMetadataRetriever
@ -246,6 +247,7 @@ class MediaUploader @Inject constructor(
private val contentResolver = context.contentResolver
@SuppressLint("Recycle") // stream is closed in ProgressRequestBody
private suspend fun upload(media: QueuedMedia): Flow<UploadEvent> {
return callbackFlow {
var mimeType = contentResolver.getType(media.uri)

View File

@ -149,7 +149,7 @@ public class NotificationHelper {
* @return the new notification
*/
@NonNull
public static android.app.Notification make(final Context context, NotificationManager notificationManager, Notification body, AccountEntity account, boolean isFirstOfBatch) {
public static android.app.Notification make(final @NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Notification body, @NonNull AccountEntity account, boolean isFirstOfBatch) {
body = body.rewriteToStatusTypeIfNeeded(account.getAccountId());
String mastodonNotificationId = body.getId();
int accountId = (int) account.getId();
@ -270,7 +270,7 @@ public class NotificationHelper {
* @param notificationManager the system's NotificationManager
* @param account the account for which the notification should be shown
*/
public static void updateSummaryNotifications(Context context, NotificationManager notificationManager, AccountEntity account) {
public static void updateSummaryNotifications(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull AccountEntity account) {
// Map from the channel ID to a list of notifications in that channel. Those are the
// notifications that will be summarised.
Map<String, List<StatusBarNotification>> channelGroups = new HashMap<>();
@ -608,7 +608,7 @@ public class NotificationHelper {
}
public static void enablePullNotifications(Context context) {
public static void enablePullNotifications(@NonNull Context context) {
WorkManager workManager = WorkManager.getInstance(context);
workManager.cancelAllWorkByTag(NOTIFICATION_PULL_TAG);
@ -636,7 +636,7 @@ public class NotificationHelper {
Log.d(TAG, "enabled notification checks with "+ PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS + "ms interval");
}
public static void disablePullNotifications(Context context) {
public static void disablePullNotifications(@NonNull Context context) {
WorkManager.getInstance(context).cancelAllWorkByTag(NOTIFICATION_PULL_TAG);
Log.d(TAG, "disabled notification checks");
}
@ -652,7 +652,7 @@ public class NotificationHelper {
}
}
public static boolean filterNotification(NotificationManager notificationManager, AccountEntity account, @NonNull Notification notification) {
public static boolean filterNotification(@NonNull NotificationManager notificationManager, @NonNull AccountEntity account, @NonNull Notification notification) {
return filterNotification(notificationManager, account, notification.getType());
}

View File

@ -474,7 +474,7 @@ abstract class SFragment : Fragment(), Injectable {
private fun requestDownloadAllMedia(status: Status) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
(activity as BaseActivity).requestPermissions(permissions) { _: Array<String?>?, grantResults: IntArray ->
(activity as BaseActivity).requestPermissions(permissions) { _, grantResults ->
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadAllMedia(status)
} else {

View File

@ -12,12 +12,11 @@
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.interfaces
package com.keylesspalace.tusky.interfaces;
public interface AccountActionListener {
void onViewAccount(String id);
void onMute(final boolean mute, final String id, final int position, final boolean notifications);
void onBlock(final boolean block, final String id, final int position);
void onRespondToFollowRequest(final boolean accept, final String id, final int position);
interface AccountActionListener {
fun onViewAccount(id: String)
fun onMute(mute: Boolean, id: String, position: Int, notifications: Boolean)
fun onBlock(block: Boolean, id: String, position: Int)
fun onRespondToFollowRequest(accept: Boolean, id: String, position: Int)
}

View File

@ -1,5 +0,0 @@
package com.keylesspalace.tusky.interfaces;
public interface PermissionRequester {
void onRequestPermissionsResult(String[] permissions, int[] grantResults);
}

View File

@ -0,0 +1,5 @@
package com.keylesspalace.tusky.interfaces
fun interface PermissionRequester {
fun onRequestPermissionsResult(permissions: Array<String>, grantResults: IntArray)
}

View File

@ -1,76 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.network;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.InputStream;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.BufferedSink;
public final class ProgressRequestBody extends RequestBody {
private final InputStream content;
private final long contentLength;
private final UploadCallback uploadListener;
private final MediaType mediaType;
private static final int DEFAULT_BUFFER_SIZE = 2048;
public interface UploadCallback {
void onProgressUpdate(int percentage);
}
public ProgressRequestBody(final InputStream content, long contentLength, final MediaType mediaType, final UploadCallback listener) {
this.content = content;
this.contentLength = contentLength;
this.mediaType = mediaType;
this.uploadListener = listener;
}
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public long contentLength() {
return contentLength;
}
@Override
public void writeTo(@NonNull BufferedSink sink) throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long uploaded = 0;
try {
int read;
while ((read = content.read(buffer)) != -1) {
uploadListener.onProgressUpdate((int)(100 * uploaded / contentLength));
uploaded += read;
sink.write(buffer, 0, read);
}
uploadListener.onProgressUpdate((int)(100 * uploaded / contentLength));
} finally {
content.close();
}
}
}

View File

@ -0,0 +1,55 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.network
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.IOException
import java.io.InputStream
class ProgressRequestBody(private val content: InputStream, private val contentLength: Long, private val mediaType: MediaType, private val uploadListener: UploadCallback) : RequestBody() {
fun interface UploadCallback {
fun onProgressUpdate(percentage: Int)
}
override fun contentType(): MediaType {
return mediaType
}
override fun contentLength(): Long {
return contentLength
}
@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var uploaded: Long = 0
content.use { content ->
var read: Int
while (content.read(buffer).also { read = it } != -1) {
uploadListener.onProgressUpdate((100 * uploaded / contentLength).toInt())
uploaded += read.toLong()
sink.write(buffer, 0, read)
}
uploadListener.onProgressUpdate((100 * uploaded / contentLength).toInt())
}
}
companion object {
private const val DEFAULT_BUFFER_SIZE = 2048
}
}

View File

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.receiver
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -39,6 +40,7 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() {
@Inject
lateinit var accountManager: AccountManager
@SuppressLint("MissingPermission")
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)

View File

@ -15,6 +15,7 @@
package com.keylesspalace.tusky.util
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.net.Uri
import java.io.Closeable
@ -34,6 +35,7 @@ fun Closeable?.closeQuietly() {
}
}
@SuppressLint("Recycle") // The linter can't tell that the stream gets closed by a helper method
fun Uri.copyToFile(
contentResolver: ContentResolver,
file: File

View File

@ -35,6 +35,7 @@
android:layout_alignTop="@+id/account_header_info"
android:background="?attr/colorPrimaryDark"
android:scaleType="centerCrop"
android:contentDescription="@string/label_header"
app:layout_collapseMode="parallax"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -279,8 +280,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawablePadding="6dp"
android:drawableStart="@drawable/ic_briefcase"
android:textSize="?attr/status_text_medium"
app:drawableStartCompat="@drawable/ic_briefcase"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Account has moved" />
@ -471,6 +472,7 @@
android:layout_height="@dimen/account_activity_avatar_size"
android:layout_marginStart="16dp"
android:padding="3dp"
android:contentDescription="@string/label_avatar"
app:layout_anchor="@+id/accountHeaderInfoContainer"
app:layout_anchorGravity="top"
app:layout_scrollFlags="scroll"

View File

@ -311,7 +311,7 @@
android:layout_marginEnd="4dp"
android:contentDescription="@string/action_toggle_visibility"
android:padding="4dp"
android:tint="?android:attr/textColorTertiary"
app:tint="?android:attr/textColorTertiary"
app:tooltipText="@string/action_toggle_visibility"
tools:src="@drawable/ic_public_24dp" />

View File

@ -66,7 +66,6 @@
android:layout_height="48dp"
android:layout_gravity="bottom"
android:background="?attr/colorPrimary"
android:drawableStart="@drawable/ic_plus_24dp"
android:drawablePadding="12dp"
android:ellipsize="end"
android:gravity="center_vertical"
@ -75,7 +74,8 @@
android:paddingEnd="8dp"
android:text="@string/action_add_tab"
android:textColor="?attr/colorOnPrimary"
android:textSize="?attr/status_text_large" />
android:textSize="?attr/status_text_large"
app:drawableStartCompat="@drawable/ic_plus_24dp" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -11,6 +11,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/filter_add_description"
android:inputType="text"
android:importantForAutofill="no"
app:layout_constraintTop_toTopOf="parent"
/>
<CheckBox

View File

@ -22,6 +22,7 @@
<!-- todo add padding -->
<ImageView
android:id="@+id/imageView"
android:contentDescription="@string/label_image"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -107,6 +107,7 @@
<Button
android:id="@+id/buttonBack"
tools:ignore="BackButton"
style="@style/TuskyButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools">
<com.keylesspalace.tusky.components.account.media.SquareImageView
android:id="@+id/accountMediaImageView"
@ -11,6 +12,7 @@
<ImageView
android:id="@+id/accountMediaImageViewOverlay"
tools:ignore="ContentDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />

View File

@ -14,7 +14,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawableStart="@drawable/ic_person_add_24dp"
android:drawablePadding="10dp"
android:ellipsize="end"
android:gravity="center_vertical"
@ -22,6 +21,7 @@
android:paddingStart="28dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
app:drawableStartCompat="@drawable/ic_person_add_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Someone followed you" />

View File

@ -43,6 +43,7 @@
android:id="@+id/notification_reporter_avatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/action_view_profile"
app:layout_constraintRight_toRightOf="@id/notification_reportee_avatar"
app:layout_constraintBottom_toBottomOf="@id/notification_reportee_avatar"
/>
@ -60,6 +61,7 @@
android:lineSpacingMultiplier="1.1"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
tools:ignore="NegativeMargin"
tools:text="30 minutes ago - 2 posts" />
<TextView

View File

@ -17,13 +17,13 @@
android:layout_marginStart="14dp"
android:layout_marginTop="@dimen/status_reblogged_bar_padding_top"
android:layout_marginEnd="14dp"
android:drawableStart="@drawable/ic_reblog_18dp"
android:drawablePadding="6dp"
android:gravity="center_vertical"
android:importantForAccessibility="no"
android:paddingStart="38dp"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium"
app:drawableStartCompat="@drawable/ic_reblog_18dp"
app:layout_constraintLeft_toRightOf="parent"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -318,6 +318,7 @@
android:layout_marginStart="-14dp"
android:contentDescription="@string/action_reply"
android:importantForAccessibility="no"
tools:ignore="NegativeMargin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/status_inset"
app:layout_constraintHorizontal_chainStyle="spread_inside"

View File

@ -1,10 +1,10 @@
<?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/status_filtered_placeholder"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:layout_height="match_parent">
<TextView
android:id="@+id/status_filter_label"
@ -16,6 +16,7 @@
android:textSize="?attr/status_text_medium"
android:textAlignment="center"
android:text="Filter: MyFilter"
tools:ignore="HardcodedText"
/>
<Button

View File

@ -43,6 +43,7 @@
android:id="@+id/notification_notification_avatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/action_view_profile"
app:layout_constraintBottom_toBottomOf="@id/notification_status_avatar"
app:layout_constraintEnd_toEndOf="@id/notification_status_avatar" />

View File

@ -19,6 +19,7 @@
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:src="@drawable/ic_drag_indicator_24dp"
tools:ignore="ContentDescription"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/textView"/>

View File

@ -6,6 +6,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/material_drawer_account_header_height"
android:clickable="true"
android:focusable="true"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<androidx.appcompat.widget.AppCompatImageView

View File

@ -12,11 +12,11 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_poll_24dp"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:text="@string/create_poll_title"
android:textStyle="bold" />
android:textStyle="bold"
app:drawableStartCompat="@drawable/ic_poll_24dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/pollPreviewOptions"

View File

@ -19,14 +19,14 @@
<item
android:id="@+id/action_mute_hashtag"
android:title="Mute"
android:title="@string/action_mute"
app:showAsAction="ifRoom"
app:iconTint="?attr/colorOnSurface"
android:icon="@drawable/ic_mute_24dp" />
<item
android:id="@+id/action_unmute_hashtag"
android:title="Unmute"
android:title="@string/action_unmute"
app:showAsAction="ifRoom"
app:iconTint="?attr/colorOnSurface"
android:icon="@drawable/ic_unmute_24dp" />

View File

@ -241,6 +241,7 @@
<string name="label_quick_reply">Reply…</string>
<string name="label_avatar">Avatar</string>
<string name="label_header">Header</string>
<string name="label_image">Image</string>
<string name="link_whats_an_instance">What\'s an instance?</string>