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
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

@ -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