#1514 Replace AsyncTask. Kotlin

This commit is contained in:
sictiru 2021-07-13 19:28:43 -07:00
parent 199d5ab932
commit 17d7cb684e
6 changed files with 426 additions and 583 deletions

View file

@ -1,16 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/register_viewswitcher"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/view_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:gravity="center"
android:inAnimation="@android:anim/fade_in"
android:outAnimation="@android:anim/fade_out" >
android:outAnimation="@android:anim/fade_out"
android:padding="30dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal" >
android:orientation="horizontal">
<ProgressBar
android:layout_width="40dp"
@ -27,18 +30,18 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="fill_parent" >
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:orientation="vertical"
android:gravity="center_horizontal" >
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/registerprogress_logo"
android:id="@+id/progress_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/logo" />
@ -46,11 +49,11 @@
</LinearLayout>
<Button
android:id="@+id/registering_next_1"
android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:text="@string/login_next"
android:textSize="20sp" />
</RelativeLayout>

View file

@ -1,45 +0,0 @@
package com.newsblur.activity;
import androidx.fragment.app.FragmentActivity;
import android.os.Bundle;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.newsblur.R;
import com.newsblur.fragment.RegisterProgressFragment;
import com.newsblur.util.PrefsUtils;
/**
* Show progress screen while registering request is being processed. This
* Activity doesn't extend NbActivity because it is one of the few
* Activities that will be shown while the user is still logged out.
*/
public class RegisterProgress extends FragmentActivity {
private FragmentManager fragmentManager;
private String currentTag = "fragment";
@Override
protected void onCreate(Bundle bundle) {
PrefsUtils.applyThemePreference(this);
super.onCreate(bundle);
setContentView(R.layout.activity_loginprogress);
fragmentManager = getSupportFragmentManager();
if (fragmentManager.findFragmentByTag(currentTag ) == null) {
final String username = getIntent().getStringExtra("username");
final String password = getIntent().getStringExtra("password");
final String email = getIntent().getStringExtra("email");
FragmentTransaction transaction = fragmentManager.beginTransaction();
RegisterProgressFragment register = RegisterProgressFragment.getInstance(username, password, email);
transaction.add(R.id.login_progress_container, register, currentTag);
transaction.commit();
}
}
}

View file

@ -0,0 +1,64 @@
package com.newsblur.activity
import android.content.Intent
import android.os.Bundle
import android.view.animation.AnimationUtils
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.newsblur.R
import com.newsblur.databinding.ActivityRegisterProgressBinding
import com.newsblur.network.APIManager
import com.newsblur.util.PrefsUtils
import com.newsblur.util.executeAsyncTask
/**
* Show progress screen while registering request is being processed. This
* Activity doesn't extend NbActivity because it is one of the few
* Activities that will be shown while the user is still logged out.
*/
class RegisterProgress : FragmentActivity() {
private lateinit var apiManager: APIManager
private lateinit var binding: ActivityRegisterProgressBinding
override fun onCreate(bundle: Bundle?) {
PrefsUtils.applyThemePreference(this)
super.onCreate(bundle)
binding = ActivityRegisterProgressBinding.inflate(layoutInflater)
setContentView(binding.root)
apiManager = APIManager(this)
val username = intent.getStringExtra("username")
val password = intent.getStringExtra("password")
val email = intent.getStringExtra("email")
binding.progressLogo.startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate))
lifecycleScope.executeAsyncTask(
doInBackground = {
apiManager.signup(username, password, email)
},
onPostExecute = {
if (it.authenticated) {
binding.viewSwitcher.showNext()
} else {
var errorMessage = it.errorMessage
if (errorMessage == null) {
errorMessage = resources.getString(R.string.register_message_error)
}
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
startActivity(Intent(this, Login::class.java))
}
}
)
binding.buttonNext.setOnClickListener { next() }
}
private operator fun next() {
val i = Intent(this, AddSocial::class.java)
startActivity(i)
}
}

View file

@ -1,110 +0,0 @@
package com.newsblur.fragment;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.Toast;
import com.newsblur.R;
import com.newsblur.activity.AddSocial;
import com.newsblur.activity.Login;
import com.newsblur.databinding.FragmentRegisterprogressBinding;
import com.newsblur.network.APIManager;
import com.newsblur.network.domain.RegisterResponse;
public class RegisterProgressFragment extends Fragment {
private APIManager apiManager;
private String username;
private String password;
private String email;
private RegisterTask registerTask;
private FragmentRegisterprogressBinding binding;
public static RegisterProgressFragment getInstance(String username, String password, String email) {
RegisterProgressFragment fragment = new RegisterProgressFragment();
Bundle bundle = new Bundle();
bundle.putString("username", username);
bundle.putString("password", password);
bundle.putString("email", email);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
apiManager = new APIManager(getActivity());
username = getArguments().getString("username");
password = getArguments().getString("password");
email = getArguments().getString("email");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_registerprogress, null);
binding = FragmentRegisterprogressBinding.bind(v);
binding.registerprogressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
if (registerTask != null) {
binding.registerViewswitcher.showNext();
} else {
registerTask = new RegisterTask();
registerTask.execute();
}
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.registeringNext1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
next();
}
});
}
private void next() {
Intent i = new Intent(getActivity(), AddSocial.class);
startActivity(i);
}
private class RegisterTask extends AsyncTask<Void, Void, RegisterResponse> {
@Override
protected RegisterResponse doInBackground(Void... params) {
return apiManager.signup(username, password, email);
}
@Override
protected void onPostExecute(RegisterResponse response) {
if (response.authenticated) {
binding.registerViewswitcher.showNext();
} else {
String errorMessage = response.getErrorMessage();
if(errorMessage == null) {
errorMessage = getResources().getString(R.string.register_message_error);
}
Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
startActivity(new Intent(getActivity(), Login.class));
}
}
}
}

View file

@ -1,417 +0,0 @@
package com.newsblur.fragment;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.newsblur.R;
import com.newsblur.activity.Profile;
import com.newsblur.domain.Comment;
import com.newsblur.domain.Reply;
import com.newsblur.domain.Story;
import com.newsblur.domain.UserDetails;
import com.newsblur.domain.UserProfile;
import com.newsblur.util.FeedUtils;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.UIUtils;
import com.newsblur.util.ViewUtils;
import com.newsblur.view.FlowLayout;
import com.newsblur.view.RoundedImageView;
public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
private ArrayList<View> topCommentViews;
private ArrayList<View> topShareViews;
private ArrayList<View> publicCommentViews;
private ArrayList<View> friendCommentViews;
private ArrayList<View> friendShareViews;
private final ReadingItemFragment fragment;
private final Story story;
private final LayoutInflater inflater;
private WeakReference<View> viewHolder;
private final Context context;
private UserDetails user;
private final FragmentManager manager;
private List<Comment> comments;
public SetupCommentSectionTask(ReadingItemFragment fragment, View view, LayoutInflater inflater, Story story) {
this.fragment = fragment;
this.context = fragment.getActivity();
this.manager = fragment.getParentFragmentManager();
this.inflater = inflater;
this.story = story;
viewHolder = new WeakReference<View>(view);
user = PrefsUtils.getUserDetails(context);
}
/**
* Do all the DB access and image view creation in the async portion of the task, saving the views in local members.
*/
@Override
protected Void doInBackground(Void... arg0) {
if (context == null) return null;
comments = FeedUtils.dbHelper.getComments(story.id);
topCommentViews = new ArrayList<View>();
topShareViews = new ArrayList<View>();
publicCommentViews = new ArrayList<View>();
friendCommentViews = new ArrayList<View>();
friendShareViews = new ArrayList<View>();
// users by whom we saw non-pseudo comments
Set<String> commentingUserIds = new HashSet<String>();
// users by whom we saw shares
Set<String> sharingUserIds = new HashSet<String>();
for (final Comment comment : comments) {
// skip public comments if they are disabled
if (!comment.byFriend && !PrefsUtils.showPublicComments(context)) {
continue;
}
UserProfile commentUser = FeedUtils.dbHelper.getUserProfile(comment.userId);
// rarely, we get a comment but never got the user's profile, so we can't display it
if (commentUser == null) {
Log.w(this.getClass().getName(), "cannot display comment from missing user ID: " + comment.userId);
continue;
}
View commentView = inflater.inflate(R.layout.include_comment, null);
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
commentText.setText(UIUtils.fromHtml(comment.commentText));
RoundedImageView commentImage = (RoundedImageView) commentView.findViewById(R.id.comment_user_image);
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
// TODO: this uses hard-coded "ago" values, which will be wrong when reading prefetched stories
if (comment.sharedDate != null) {
commentSharedDate.setText(comment.sharedDate + " ago");
}
final FlowLayout favouriteContainer = (FlowLayout) commentView.findViewById(R.id.comment_favourite_avatars);
final ImageView favouriteIcon = (ImageView) commentView.findViewById(R.id.comment_favourite_icon);
final ImageView replyIcon = (ImageView) commentView.findViewById(R.id.comment_reply_icon);
if (comment.likingUsers != null) {
if (Arrays.asList(comment.likingUsers).contains(user.id)) {
favouriteIcon.setImageResource(R.drawable.ic_star_active);
}
for (String id : comment.likingUsers) {
RoundedImageView favouriteImage = new RoundedImageView(context);
UserProfile user = FeedUtils.dbHelper.getUserProfile(id);
if (user != null) {
FeedUtils.iconLoader.displayImage(user.photoUrl, favouriteImage, false);
favouriteContainer.addView(favouriteImage);
}
}
// users cannot fave their own comments. attempting to do so will actually queue a fatally invalid API call
if (TextUtils.equals(comment.userId, user.id)) {
favouriteIcon.setVisibility(View.GONE);
} else {
favouriteIcon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!Arrays.asList(comment.likingUsers).contains(user.id)) {
FeedUtils.likeComment(story, comment.userId, context);
} else {
FeedUtils.unlikeComment(story, comment.userId, context);
}
}
});
}
}
if (comment.isPlaceholder) {
replyIcon.setVisibility(View.INVISIBLE);
} else {
replyIcon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (story != null) {
UserProfile user = FeedUtils.dbHelper.getUserProfile(comment.userId);
if (user != null) {
DialogFragment newFragment = ReplyDialogFragment.newInstance(story, comment.userId, user.username);
newFragment.show(manager, "dialog");
}
}
}
});
}
List<Reply> replies = FeedUtils.dbHelper.getCommentReplies(comment.id);
for (final Reply reply : replies) {
View replyView = inflater.inflate(R.layout.include_reply, null);
TextView replyText = (TextView) replyView.findViewById(R.id.reply_text);
replyText.setText(UIUtils.fromHtml(reply.text));
RoundedImageView replyImage = (RoundedImageView) replyView.findViewById(R.id.reply_user_image);
final UserProfile replyUser = FeedUtils.dbHelper.getUserProfile(reply.userId);
if (replyUser != null) {
FeedUtils.iconLoader.displayImage(replyUser.photoUrl, replyImage, false);
replyImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(context, Profile.class);
i.putExtra(Profile.USER_ID, replyUser.userId);
context.startActivity(i);
}
});
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
replyUsername.setText(replyUser.username);
} else {
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
replyUsername.setText(R.string.unknown_user);
}
if (reply.shortDate != null) {
TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate);
replySharedDate.setText(reply.shortDate + " ago");
}
ImageView editIcon = (ImageView) replyView.findViewById(R.id.reply_edit_icon);
if (TextUtils.equals(reply.userId, user.id)) {
editIcon.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (story != null) {
DialogFragment newFragment = EditReplyDialogFragment.newInstance(story, comment.userId, reply.id, reply.text);
newFragment.show(manager, "dialog");
}
}
});
} else {
editIcon.setVisibility(View.INVISIBLE);
}
((LinearLayout) commentView.findViewById(R.id.comment_replies_container)).addView(replyView);
}
TextView commentUsername = (TextView) commentView.findViewById(R.id.comment_username);
commentUsername.setText(commentUser.username);
String userPhoto = commentUser.photoUrl;
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
if (!TextUtils.isEmpty(commentUser.location)) {
commentLocation.setText(commentUser.location.toUpperCase());
} else {
commentLocation.setVisibility(View.GONE);
}
if (!TextUtils.isEmpty(comment.sourceUserId)) {
commentImage.setVisibility(View.INVISIBLE);
RoundedImageView usershareImage = (RoundedImageView) commentView.findViewById(R.id.comment_user_reshare_image);
RoundedImageView sourceUserImage = (RoundedImageView) commentView.findViewById(R.id.comment_sharesource_image);
sourceUserImage.setVisibility(View.VISIBLE);
usershareImage.setVisibility(View.VISIBLE);
commentImage.setVisibility(View.INVISIBLE);
UserProfile sourceUser = FeedUtils.dbHelper.getUserProfile(comment.sourceUserId);
if (sourceUser != null) {
FeedUtils.iconLoader.displayImage(sourceUser.photoUrl, sourceUserImage, false);
FeedUtils.iconLoader.displayImage(userPhoto, usershareImage, false);
}
} else {
FeedUtils.iconLoader.displayImage(userPhoto, commentImage, false);
}
commentImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent i = new Intent(context, Profile.class);
i.putExtra(Profile.USER_ID, comment.userId);
context.startActivity(i);
}
});
if (comment.isPseudo) {
friendShareViews.add(commentView);
sharingUserIds.add(comment.userId);
} else if (comment.byFriend) {
friendCommentViews.add(commentView);
} else {
publicCommentViews.add(commentView);
}
// for actual comments, also populate the upper icon bar
if (!comment.isPseudo) {
ImageView image = ViewUtils.createSharebarImage(context, commentUser.photoUrl, commentUser.userId);
topCommentViews.add(image);
commentingUserIds.add(comment.userId);
}
}
// the story object supplements the pseudo-comment share list
for (String userId : story.sharedUserIds) {
// for the purpose of this top-line share list, exclude non-pseudo comments
if (!commentingUserIds.contains(userId)) {
sharingUserIds.add(userId);
}
}
// now that we have all shares from the comments table and story object, populate the shares row
for (String userId : sharingUserIds) {
UserProfile user = FeedUtils.dbHelper.getUserProfile(userId);
if (user == null) {
Log.w(this.getClass().getName(), "cannot display share from missing user ID: " + userId);
continue;
}
ImageView image = ViewUtils.createSharebarImage(context, user.photoUrl, user.userId);
topShareViews.add(image);
}
return null;
}
/**
* Push all the pre-created views into the actual UI.
*/
protected void onPostExecute(Void result) {
if (context == null) return;
View view = viewHolder.get();
if (view == null) return; // fragment was dismissed before we rendered
TextView headerCommentTotal = (TextView) view.findViewById(R.id.comment_by);
TextView headerShareTotal = (TextView) view.findViewById(R.id.shared_by);
FlowLayout sharedGrid = (FlowLayout) view.findViewById(R.id.reading_social_shareimages);
FlowLayout commentGrid = (FlowLayout) view.findViewById(R.id.reading_social_commentimages);
TextView friendCommentTotal = ((TextView) view.findViewById(R.id.reading_friend_comment_total));
TextView friendShareTotal = ((TextView) view.findViewById(R.id.reading_friend_emptyshare_total));
TextView publicCommentTotal = ((TextView) view.findViewById(R.id.reading_public_comment_total));
int publicCommentCount = publicCommentViews.size();
int friendCommentCount = friendCommentViews.size();
int friendShareCount = friendShareViews.size();
int allCommentCount = topCommentViews.size();
int allShareCount = topShareViews.size();
if (allCommentCount > 0 || allShareCount > 0) {
view.findViewById(R.id.reading_share_bar).setVisibility(View.VISIBLE);
view.findViewById(R.id.share_bar_underline).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.reading_share_bar).setVisibility(View.GONE);
view.findViewById(R.id.share_bar_underline).setVisibility(View.GONE);
}
sharedGrid.removeAllViews();
for (View image : topShareViews) {
sharedGrid.addView(image);
}
commentGrid.removeAllViews();
for (View image : topCommentViews) {
commentGrid.addView(image);
}
if (allCommentCount > 0) {
String countText = context.getString(R.string.friends_comments_count);
if (allCommentCount == 1) {
countText = countText.substring(0, countText.length() - 1);
}
headerCommentTotal.setText(String.format(countText, allCommentCount));
headerCommentTotal.setVisibility(View.VISIBLE);
} else {
headerCommentTotal.setVisibility(View.GONE);
}
if (allShareCount > 0) {
String countText = context.getString(R.string.friends_shares_count);
if (allShareCount == 1) {
countText = countText.substring(0, countText.length() - 1);
}
headerShareTotal.setText(String.format(countText, allShareCount));
headerShareTotal.setVisibility(View.VISIBLE);
} else {
headerShareTotal.setVisibility(View.GONE);
}
if (publicCommentCount > 0) {
String commentCount = context.getString(R.string.public_comment_count);
if (publicCommentCount == 1) {
commentCount = commentCount.substring(0, commentCount.length() - 1);
}
publicCommentTotal.setText(String.format(commentCount, publicCommentCount));
view.findViewById(R.id.reading_public_comment_header).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.reading_public_comment_header).setVisibility(View.GONE);
}
if (friendCommentCount > 0) {
String commentCount = context.getString(R.string.friends_comments_count);
if (friendCommentCount == 1) {
commentCount = commentCount.substring(0, commentCount.length() - 1);
}
friendCommentTotal.setText(String.format(commentCount, friendCommentCount));
view.findViewById(R.id.reading_friend_comment_header).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.reading_friend_comment_header).setVisibility(View.GONE);
}
if (friendShareCount > 0) {
String commentCount = context.getString(R.string.friends_shares_count);
if (friendShareCount == 1) {
commentCount = commentCount.substring(0, commentCount.length() - 1);
}
friendShareTotal.setText(String.format(commentCount, friendShareCount));
view.findViewById(R.id.reading_friend_emptyshare_header).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.reading_friend_emptyshare_header).setVisibility(View.GONE);
}
LinearLayout publicCommentListContainer = (LinearLayout) view.findViewById(R.id.reading_public_comment_container);
publicCommentListContainer.removeAllViews();
for (int i = 0; i < publicCommentViews.size(); i++) {
if (i == publicCommentViews.size() - 1) {
publicCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
}
publicCommentListContainer.addView(publicCommentViews.get(i));
}
LinearLayout friendCommentListContainer = (LinearLayout) view.findViewById(R.id.reading_friend_comment_container);
friendCommentListContainer.removeAllViews();
for (int i = 0; i < friendCommentViews.size(); i++) {
if (i == friendCommentViews.size() - 1) {
friendCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
}
friendCommentListContainer.addView(friendCommentViews.get(i));
}
LinearLayout friendShareListContainer = (LinearLayout) view.findViewById(R.id.reading_friend_emptyshare_container);
friendShareListContainer.removeAllViews();
for (int i = 0; i < friendShareViews.size(); i++) {
if (i == friendShareViews.size() - 1) {
friendShareViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
}
friendShareListContainer.addView(friendShareViews.get(i));
}
fragment.onSocialLoadFinished();
}
}

View file

@ -0,0 +1,348 @@
package com.newsblur.fragment
import android.content.Context
import android.content.Intent
import android.text.TextUtils
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope
import com.newsblur.R
import com.newsblur.activity.Profile
import com.newsblur.domain.Comment
import com.newsblur.domain.Story
import com.newsblur.domain.UserDetails
import com.newsblur.util.*
import com.newsblur.view.FlowLayout
import com.newsblur.view.RoundedImageView
import java.lang.ref.WeakReference
import java.util.*
class SetupCommentSectionTask(private val fragment: ReadingItemFragment, view: View, inflater: LayoutInflater, story: Story?) {
private var topCommentViews: ArrayList<View>? = null
private var topShareViews: ArrayList<View>? = null
private var publicCommentViews: ArrayList<View>? = null
private var friendCommentViews: ArrayList<View>? = null
private var friendShareViews: ArrayList<View>? = null
private val story: Story?
private val inflater: LayoutInflater
private val viewHolder: WeakReference<View>
private val context: Context?
private val user: UserDetails
private val manager: FragmentManager
private var comments: MutableList<Comment>? = null
/**
* Do all the DB access and image view creation in the async portion of the task, saving the views in local members.
*/
fun execute() {
fragment.lifecycleScope.executeAsyncTask(
doInBackground = {
doInBackground()
},
onPostExecute = {
onPostExecute()
}
)
}
private fun doInBackground() {
if (context == null || story == null) return
comments = FeedUtils.dbHelper.getComments(story.id)
topCommentViews = ArrayList()
topShareViews = ArrayList()
publicCommentViews = ArrayList()
friendCommentViews = ArrayList()
friendShareViews = ArrayList()
// users by whom we saw non-pseudo comments
val commentingUserIds: MutableSet<String> = HashSet()
// users by whom we saw shares
val sharingUserIds: MutableSet<String> = HashSet()
for (comment in comments!!) {
// skip public comments if they are disabled
if (!comment.byFriend && !PrefsUtils.showPublicComments(context)) {
continue
}
val commentUser = FeedUtils.dbHelper.getUserProfile(comment.userId)
// rarely, we get a comment but never got the user's profile, so we can't display it
if (commentUser == null) {
Log.w(this.javaClass.name, "cannot display comment from missing user ID: " + comment.userId)
continue
}
val commentView = inflater.inflate(R.layout.include_comment, null)
val commentText = commentView.findViewById<View>(R.id.comment_text) as TextView
commentText.text = UIUtils.fromHtml(comment.commentText)
val commentImage = commentView.findViewById<View>(R.id.comment_user_image) as RoundedImageView
val commentSharedDate = commentView.findViewById<View>(R.id.comment_shareddate) as TextView
// TODO: this uses hard-coded "ago" values, which will be wrong when reading prefetched stories
if (comment.sharedDate != null) {
commentSharedDate.text = comment.sharedDate + " ago"
}
val favouriteContainer = commentView.findViewById<View>(R.id.comment_favourite_avatars) as FlowLayout
val favouriteIcon = commentView.findViewById<View>(R.id.comment_favourite_icon) as ImageView
val replyIcon = commentView.findViewById<View>(R.id.comment_reply_icon) as ImageView
if (comment.likingUsers != null) {
if (mutableListOf<String>(*comment.likingUsers).contains(user.id)) {
favouriteIcon.setImageResource(R.drawable.ic_star_active)
}
for (id in comment.likingUsers) {
val favouriteImage = RoundedImageView(context)
val user = FeedUtils.dbHelper.getUserProfile(id)
if (user != null) {
FeedUtils.iconLoader.displayImage(user.photoUrl, favouriteImage, false)
favouriteContainer.addView(favouriteImage)
}
}
// users cannot fave their own comments. attempting to do so will actually queue a fatally invalid API call
if (TextUtils.equals(comment.userId, user.id)) {
favouriteIcon.visibility = View.GONE
} else {
favouriteIcon.setOnClickListener {
if (!mutableListOf<String>(*comment.likingUsers).contains(user.id)) {
FeedUtils.likeComment(story, comment.userId, context)
} else {
FeedUtils.unlikeComment(story, comment.userId, context)
}
}
}
}
if (comment.isPlaceholder) {
replyIcon.visibility = View.INVISIBLE
} else {
replyIcon.setOnClickListener {
val user = FeedUtils.dbHelper.getUserProfile(comment.userId)
if (user != null) {
val newFragment: DialogFragment = ReplyDialogFragment.newInstance(story, comment.userId, user.username)
newFragment.show(manager, "dialog")
}
}
}
val replies = FeedUtils.dbHelper.getCommentReplies(comment.id)
for (reply in replies) {
val replyView = inflater.inflate(R.layout.include_reply, null)
val replyText = replyView.findViewById<View>(R.id.reply_text) as TextView
replyText.text = UIUtils.fromHtml(reply.text)
val replyImage = replyView.findViewById<View>(R.id.reply_user_image) as RoundedImageView
val replyUser = FeedUtils.dbHelper.getUserProfile(reply.userId)
if (replyUser != null) {
FeedUtils.iconLoader.displayImage(replyUser.photoUrl, replyImage, false)
replyImage.setOnClickListener {
val i = Intent(context, Profile::class.java)
i.putExtra(Profile.USER_ID, replyUser.userId)
context.startActivity(i)
}
val replyUsername = replyView.findViewById<View>(R.id.reply_username) as TextView
replyUsername.text = replyUser.username
} else {
val replyUsername = replyView.findViewById<View>(R.id.reply_username) as TextView
replyUsername.setText(R.string.unknown_user)
}
if (reply.shortDate != null) {
val replySharedDate = replyView.findViewById<View>(R.id.reply_shareddate) as TextView
replySharedDate.text = reply.shortDate + " ago"
}
val editIcon = replyView.findViewById<View>(R.id.reply_edit_icon) as ImageView
if (TextUtils.equals(reply.userId, user.id)) {
editIcon.setOnClickListener {
val newFragment: DialogFragment = EditReplyDialogFragment.newInstance(story, comment.userId, reply.id, reply.text)
newFragment.show(manager, "dialog")
}
} else {
editIcon.visibility = View.INVISIBLE
}
(commentView.findViewById<View>(R.id.comment_replies_container) as LinearLayout).addView(replyView)
}
val commentUsername = commentView.findViewById<View>(R.id.comment_username) as TextView
commentUsername.text = commentUser.username
val userPhoto = commentUser.photoUrl
val commentLocation = commentView.findViewById<View>(R.id.comment_location) as TextView
if (!TextUtils.isEmpty(commentUser.location)) {
commentLocation.text = commentUser.location.uppercase()
} else {
commentLocation.visibility = View.GONE
}
if (!TextUtils.isEmpty(comment.sourceUserId)) {
commentImage.visibility = View.INVISIBLE
val usershareImage = commentView.findViewById<View>(R.id.comment_user_reshare_image) as RoundedImageView
val sourceUserImage = commentView.findViewById<View>(R.id.comment_sharesource_image) as RoundedImageView
sourceUserImage.visibility = View.VISIBLE
usershareImage.visibility = View.VISIBLE
commentImage.visibility = View.INVISIBLE
val sourceUser = FeedUtils.dbHelper.getUserProfile(comment.sourceUserId)
if (sourceUser != null) {
FeedUtils.iconLoader.displayImage(sourceUser.photoUrl, sourceUserImage, false)
FeedUtils.iconLoader.displayImage(userPhoto, usershareImage, false)
}
} else {
FeedUtils.iconLoader.displayImage(userPhoto, commentImage, false)
}
commentImage.setOnClickListener {
val i = Intent(context, Profile::class.java)
i.putExtra(Profile.USER_ID, comment.userId)
context.startActivity(i)
}
if (comment.isPseudo) {
friendShareViews!!.add(commentView)
sharingUserIds.add(comment.userId)
} else if (comment.byFriend) {
friendCommentViews!!.add(commentView)
} else {
publicCommentViews!!.add(commentView)
}
// for actual comments, also populate the upper icon bar
if (!comment.isPseudo) {
val image = ViewUtils.createSharebarImage(context, commentUser.photoUrl, commentUser.userId)
topCommentViews!!.add(image)
commentingUserIds.add(comment.userId)
}
}
// the story object supplements the pseudo-comment share list
for (userId in story.sharedUserIds) {
// for the purpose of this top-line share list, exclude non-pseudo comments
if (!commentingUserIds.contains(userId)) {
sharingUserIds.add(userId)
}
}
// now that we have all shares from the comments table and story object, populate the shares row
for (userId in sharingUserIds) {
val user = FeedUtils.dbHelper.getUserProfile(userId)
if (user == null) {
Log.w(this.javaClass.name, "cannot display share from missing user ID: $userId")
continue
}
val image = ViewUtils.createSharebarImage(context, user.photoUrl, user.userId)
topShareViews!!.add(image)
}
}
/**
* Push all the pre-created views into the actual UI.
*/
private fun onPostExecute() {
if (context == null) return
val view = viewHolder.get() ?: return
// fragment was dismissed before we rendered
val headerCommentTotal = view.findViewById<View>(R.id.comment_by) as TextView
val headerShareTotal = view.findViewById<View>(R.id.shared_by) as TextView
val sharedGrid = view.findViewById<View>(R.id.reading_social_shareimages) as FlowLayout
val commentGrid = view.findViewById<View>(R.id.reading_social_commentimages) as FlowLayout
val friendCommentTotal = view.findViewById<View>(R.id.reading_friend_comment_total) as TextView
val friendShareTotal = view.findViewById<View>(R.id.reading_friend_emptyshare_total) as TextView
val publicCommentTotal = view.findViewById<View>(R.id.reading_public_comment_total) as TextView
val publicCommentCount = publicCommentViews!!.size
val friendCommentCount = friendCommentViews!!.size
val friendShareCount = friendShareViews!!.size
val allCommentCount = topCommentViews!!.size
val allShareCount = topShareViews!!.size
if (allCommentCount > 0 || allShareCount > 0) {
view.findViewById<View>(R.id.reading_share_bar).visibility = View.VISIBLE
view.findViewById<View>(R.id.share_bar_underline).visibility = View.VISIBLE
} else {
view.findViewById<View>(R.id.reading_share_bar).visibility = View.GONE
view.findViewById<View>(R.id.share_bar_underline).visibility = View.GONE
}
sharedGrid.removeAllViews()
for (image in topShareViews!!) {
sharedGrid.addView(image)
}
commentGrid.removeAllViews()
for (image in topCommentViews!!) {
commentGrid.addView(image)
}
if (allCommentCount > 0) {
var countText = context.getString(R.string.friends_comments_count)
if (allCommentCount == 1) {
countText = countText.substring(0, countText.length - 1)
}
headerCommentTotal.text = String.format(countText, allCommentCount)
headerCommentTotal.visibility = View.VISIBLE
} else {
headerCommentTotal.visibility = View.GONE
}
if (allShareCount > 0) {
var countText = context.getString(R.string.friends_shares_count)
if (allShareCount == 1) {
countText = countText.substring(0, countText.length - 1)
}
headerShareTotal.text = String.format(countText, allShareCount)
headerShareTotal.visibility = View.VISIBLE
} else {
headerShareTotal.visibility = View.GONE
}
if (publicCommentCount > 0) {
var commentCount = context.getString(R.string.public_comment_count)
if (publicCommentCount == 1) {
commentCount = commentCount.substring(0, commentCount.length - 1)
}
publicCommentTotal.text = String.format(commentCount, publicCommentCount)
view.findViewById<View>(R.id.reading_public_comment_header).visibility = View.VISIBLE
} else {
view.findViewById<View>(R.id.reading_public_comment_header).visibility = View.GONE
}
if (friendCommentCount > 0) {
var commentCount = context.getString(R.string.friends_comments_count)
if (friendCommentCount == 1) {
commentCount = commentCount.substring(0, commentCount.length - 1)
}
friendCommentTotal.text = String.format(commentCount, friendCommentCount)
view.findViewById<View>(R.id.reading_friend_comment_header).visibility = View.VISIBLE
} else {
view.findViewById<View>(R.id.reading_friend_comment_header).visibility = View.GONE
}
if (friendShareCount > 0) {
var commentCount = context.getString(R.string.friends_shares_count)
if (friendShareCount == 1) {
commentCount = commentCount.substring(0, commentCount.length - 1)
}
friendShareTotal.text = String.format(commentCount, friendShareCount)
view.findViewById<View>(R.id.reading_friend_emptyshare_header).visibility = View.VISIBLE
} else {
view.findViewById<View>(R.id.reading_friend_emptyshare_header).visibility = View.GONE
}
val publicCommentListContainer = view.findViewById<View>(R.id.reading_public_comment_container) as LinearLayout
publicCommentListContainer.removeAllViews()
for (i in publicCommentViews!!.indices) {
if (i == publicCommentViews!!.size - 1) {
publicCommentViews!![i].findViewById<View>(R.id.comment_divider).visibility = View.GONE
}
publicCommentListContainer.addView(publicCommentViews!![i])
}
val friendCommentListContainer = view.findViewById<View>(R.id.reading_friend_comment_container) as LinearLayout
friendCommentListContainer.removeAllViews()
for (i in friendCommentViews!!.indices) {
if (i == friendCommentViews!!.size - 1) {
friendCommentViews!![i].findViewById<View>(R.id.comment_divider).visibility = View.GONE
}
friendCommentListContainer.addView(friendCommentViews!![i])
}
val friendShareListContainer = view.findViewById<View>(R.id.reading_friend_emptyshare_container) as LinearLayout
friendShareListContainer.removeAllViews()
for (i in friendShareViews!!.indices) {
if (i == friendShareViews!!.size - 1) {
friendShareViews!![i].findViewById<View>(R.id.comment_divider).visibility = View.GONE
}
friendShareListContainer.addView(friendShareViews!![i])
}
fragment.onSocialLoadFinished()
}
init {
context = fragment.requireContext()
manager = fragment.parentFragmentManager
this.inflater = inflater
this.story = story
viewHolder = WeakReference(view)
user = PrefsUtils.getUserDetails(context)
}
}