mirror of https://github.com/ThmmyNoLife/mTHMMY
committed by
GitHub
37 changed files with 2324 additions and 518 deletions
@ -0,0 +1,126 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.create_pm; |
||||
|
|
||||
|
import android.content.Intent; |
||||
|
import android.content.SharedPreferences; |
||||
|
import android.os.Bundle; |
||||
|
import android.preference.PreferenceManager; |
||||
|
import android.text.InputType; |
||||
|
import android.text.TextUtils; |
||||
|
import android.view.View; |
||||
|
import android.view.inputmethod.EditorInfo; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import com.google.android.material.textfield.TextInputLayout; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; |
||||
|
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; |
||||
|
import gr.thmmy.mthmmy.base.BaseActivity; |
||||
|
import gr.thmmy.mthmmy.editorview.EditorView; |
||||
|
import gr.thmmy.mthmmy.editorview.EmojiKeyboard; |
||||
|
import gr.thmmy.mthmmy.session.SessionManager; |
||||
|
import gr.thmmy.mthmmy.utils.ExternalAsyncTask; |
||||
|
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
public class CreatePMActivity extends BaseActivity implements ExternalAsyncTask.OnTaskStartedListener, ExternalAsyncTask.OnTaskFinishedListener<Boolean> { |
||||
|
|
||||
|
private MaterialProgressBar progressBar; |
||||
|
private EditorView contentEditor; |
||||
|
private TextInputLayout subjectInput; |
||||
|
private EmojiKeyboard emojiKeyboard; |
||||
|
private String username, sendPmUrl; |
||||
|
/** |
||||
|
* Used for example in quotes to pre-populate the EditorView with quoted text |
||||
|
*/ |
||||
|
private String defaultContent; |
||||
|
|
||||
|
public static final String BUNDLE_SEND_PM_URL = "send-pm-url"; |
||||
|
public static final String BUNDLE_PM_CONTENT = "pm-content"; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.activity_create_pm); |
||||
|
|
||||
|
Intent callingIntent = getIntent(); |
||||
|
username = callingIntent.getStringExtra(ProfileActivity.BUNDLE_PROFILE_USERNAME); |
||||
|
sendPmUrl = callingIntent.getStringExtra(BUNDLE_SEND_PM_URL); |
||||
|
defaultContent = callingIntent.getStringExtra(BUNDLE_PM_CONTENT); |
||||
|
|
||||
|
//Initialize toolbar
|
||||
|
toolbar = findViewById(R.id.toolbar); |
||||
|
toolbar.setTitle("Create topic"); |
||||
|
setSupportActionBar(toolbar); |
||||
|
if (getSupportActionBar() != null) { |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true); |
||||
|
} |
||||
|
|
||||
|
progressBar = findViewById(R.id.progressBar); |
||||
|
|
||||
|
emojiKeyboard = findViewById(R.id.emoji_keyboard); |
||||
|
|
||||
|
subjectInput = findViewById(R.id.subject_input); |
||||
|
subjectInput.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT); |
||||
|
subjectInput.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE); |
||||
|
|
||||
|
contentEditor = findViewById(R.id.main_content_editorview); |
||||
|
contentEditor.setEmojiKeyboard(emojiKeyboard); |
||||
|
emojiKeyboard.registerEmojiInputField(contentEditor); |
||||
|
contentEditor.setOnSubmitListener(v -> { |
||||
|
if (TextUtils.isEmpty(subjectInput.getEditText().getText())) { |
||||
|
subjectInput.setError("Required"); |
||||
|
return; |
||||
|
} |
||||
|
if (TextUtils.isEmpty(contentEditor.getText())) { |
||||
|
contentEditor.setError("Required"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
boolean includeAppSignature = true; |
||||
|
SessionManager sessionManager = BaseActivity.getSessionManager(); |
||||
|
if (sessionManager.isLoggedIn()) { |
||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); |
||||
|
includeAppSignature = prefs.getBoolean(SettingsActivity.POSTING_APP_SIGNATURE_ENABLE_KEY, true); |
||||
|
} |
||||
|
|
||||
|
SendPMTask sendPMTask = new SendPMTask(includeAppSignature); |
||||
|
sendPMTask.setOnTaskStartedListener(this); |
||||
|
sendPMTask.setOnTaskFinishedListener(this); |
||||
|
sendPMTask.execute(sendPmUrl, subjectInput.getEditText().getText().toString(), |
||||
|
contentEditor.getText().toString()); |
||||
|
}); |
||||
|
if (defaultContent != null) |
||||
|
contentEditor.setText(defaultContent); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBackPressed() { |
||||
|
if (emojiKeyboard.getVisibility() == View.VISIBLE) { |
||||
|
emojiKeyboard.setVisibility(View.GONE); |
||||
|
} else { |
||||
|
super.onBackPressed(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void onTaskStarted() { |
||||
|
Timber.i("New pm started being sent"); |
||||
|
progressBar.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onTaskFinished(Boolean success) { |
||||
|
progressBar.setVisibility(View.INVISIBLE); |
||||
|
if (success) { |
||||
|
Timber.i("New pm sent successfully"); |
||||
|
Toast.makeText(this, "Personal message sent successfully", Toast.LENGTH_SHORT).show(); |
||||
|
finish(); |
||||
|
} else { |
||||
|
Timber.w("Failed to send pm"); |
||||
|
Toast.makeText(getBaseContext(), "Failed to send PM. Check your connection", Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.create_pm; |
||||
|
|
||||
|
import org.jsoup.Jsoup; |
||||
|
import org.jsoup.nodes.Document; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.base.BaseApplication; |
||||
|
import gr.thmmy.mthmmy.utils.ExternalAsyncTask; |
||||
|
import okhttp3.MultipartBody; |
||||
|
import okhttp3.OkHttpClient; |
||||
|
import okhttp3.Request; |
||||
|
import okhttp3.RequestBody; |
||||
|
import okhttp3.Response; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus; |
||||
|
|
||||
|
public class SendPMTask extends ExternalAsyncTask<String, Boolean> { |
||||
|
|
||||
|
private boolean includeAppSignature; |
||||
|
|
||||
|
public SendPMTask(boolean includeAppSignature) { |
||||
|
this.includeAppSignature = includeAppSignature; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Boolean doInBackground(String... strings) { |
||||
|
Request request = new Request.Builder() |
||||
|
.url(strings[0] + ";wap2") |
||||
|
.build(); |
||||
|
|
||||
|
OkHttpClient client = BaseApplication.getInstance().getClient(); |
||||
|
|
||||
|
Document document; |
||||
|
String seqnum, sc, outbox, createTopicUrl, replied_to, folder, u; |
||||
|
try { |
||||
|
Response response = client.newCall(request).execute(); |
||||
|
document = Jsoup.parse(response.body().string()); |
||||
|
|
||||
|
seqnum = document.select("input[name=seqnum]").first().attr("value"); |
||||
|
sc = document.select("input[name=sc]").first().attr("value"); |
||||
|
outbox = document.select("input[name=outbox]").first().attr("value"); |
||||
|
replied_to = document.select("input[name=replied_to]").first().attr("value"); |
||||
|
folder = document.select("input[name=folder]").first().attr("value"); |
||||
|
u = document.select("input[name=u]").first().attr("value"); |
||||
|
createTopicUrl = document.select("form").first().attr("action"); |
||||
|
|
||||
|
final String appSignature = "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/" + |
||||
|
"details?id=gr.thmmy.mthmmy]mTHMMY[/url] [/i][/size][/right]"; |
||||
|
|
||||
|
RequestBody postBody = new MultipartBody.Builder() |
||||
|
.setType(MultipartBody.FORM) |
||||
|
.addFormDataPart("message", strings[2] + (includeAppSignature ? appSignature : "")) |
||||
|
.addFormDataPart("seqnum", seqnum) |
||||
|
.addFormDataPart("sc", sc) |
||||
|
.addFormDataPart("u", u) // recipient id
|
||||
|
.addFormDataPart("subject", strings[1]) |
||||
|
.addFormDataPart("outbox", outbox) |
||||
|
.addFormDataPart("replied_to", replied_to) |
||||
|
.addFormDataPart("folder", folder) |
||||
|
.build(); |
||||
|
|
||||
|
Request pmRequest = new Request.Builder() |
||||
|
.url(createTopicUrl) |
||||
|
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36") |
||||
|
.post(postBody) |
||||
|
.build(); |
||||
|
|
||||
|
try { |
||||
|
client.newCall(pmRequest).execute(); |
||||
|
Response response2 = client.newCall(pmRequest).execute(); |
||||
|
switch (replyStatus(response2)) { |
||||
|
case SUCCESSFUL: |
||||
|
BaseApplication.getInstance().logFirebaseAnalyticsEvent("new_pm_sent", null); |
||||
|
return true; |
||||
|
default: |
||||
|
Timber.e("Malformed pmRequest. Request string: %s", pmRequest.toString()); |
||||
|
return false; |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
return false; |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package gr.thmmy.mthmmy.activities.create_content; |
package gr.thmmy.mthmmy.activities.create_topic; |
||||
|
|
||||
import android.os.AsyncTask; |
import android.os.AsyncTask; |
||||
|
|
@ -0,0 +1,94 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.inbox; |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
import android.view.View; |
||||
|
import android.widget.ImageButton; |
||||
|
import android.widget.ProgressBar; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.lifecycle.ViewModel; |
||||
|
import androidx.lifecycle.ViewModelProvider; |
||||
|
import androidx.lifecycle.ViewModelProviders; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.base.BaseActivity; |
||||
|
import gr.thmmy.mthmmy.pagination.BottomPaginationView; |
||||
|
import gr.thmmy.mthmmy.utils.NetworkResultCodes; |
||||
|
import gr.thmmy.mthmmy.viewmodel.InboxViewModel; |
||||
|
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
public class InboxActivity extends BaseActivity { |
||||
|
|
||||
|
private InboxViewModel inboxViewModel; |
||||
|
|
||||
|
private MaterialProgressBar progressBar; |
||||
|
private RecyclerView pmRecyclerview; |
||||
|
private InboxAdapter inboxAdapter; |
||||
|
private BottomPaginationView bottomPagination; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.activity_inbox); |
||||
|
|
||||
|
//Initialize toolbar
|
||||
|
toolbar = findViewById(R.id.toolbar); |
||||
|
toolbar.setTitle("Inbox"); |
||||
|
setSupportActionBar(toolbar); |
||||
|
if (getSupportActionBar() != null) { |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true); |
||||
|
} |
||||
|
|
||||
|
createDrawer(); |
||||
|
drawer.setSelection(INBOX_ID); |
||||
|
|
||||
|
progressBar = findViewById(R.id.progress_bar); |
||||
|
pmRecyclerview = findViewById(R.id.inbox_recyclerview); |
||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this); |
||||
|
pmRecyclerview.setLayoutManager(layoutManager); |
||||
|
inboxAdapter = new InboxAdapter(this); |
||||
|
pmRecyclerview.setAdapter(inboxAdapter); |
||||
|
bottomPagination = findViewById(R.id.bottom_pagination); |
||||
|
|
||||
|
inboxViewModel = new ViewModelProvider(this).get(InboxViewModel.class); |
||||
|
bottomPagination.setOnPageRequestedListener(inboxViewModel); |
||||
|
subscribeUI(); |
||||
|
|
||||
|
inboxViewModel.loadInbox(); |
||||
|
} |
||||
|
|
||||
|
private void subscribeUI() { |
||||
|
inboxViewModel.setOnInboxTaskStartedListener(() -> { |
||||
|
progressBar.setVisibility(View.VISIBLE); |
||||
|
Timber.d("inbox task started"); |
||||
|
}); |
||||
|
inboxViewModel.setOnInboxTaskFinishedListener((resultCode, inbox) -> { |
||||
|
progressBar.setVisibility(View.INVISIBLE); |
||||
|
if (resultCode == NetworkResultCodes.SUCCESSFUL) { |
||||
|
Timber.i("Successfully loaded inbox"); |
||||
|
inboxAdapter.notifyDataSetChanged(); |
||||
|
} else { |
||||
|
Timber.w("Failed to load inbox"); |
||||
|
Toast.makeText(this, "Failed to load inbox", Toast.LENGTH_SHORT).show(); |
||||
|
finish(); |
||||
|
} |
||||
|
}); |
||||
|
inboxViewModel.setOnInboxTaskCancelledListener(() -> { |
||||
|
progressBar.setVisibility(ProgressBar.GONE); |
||||
|
Timber.d("inbox task cancelled"); |
||||
|
}); |
||||
|
inboxViewModel.getPageIndicatorIndex().observe(this, pageIndicatorIndex -> { |
||||
|
if (pageIndicatorIndex == null) return; |
||||
|
bottomPagination.setIndicatedPageIndex(pageIndicatorIndex); |
||||
|
}); |
||||
|
inboxViewModel.getPageCount().observe(this, pageCount -> { |
||||
|
if (pageCount == null) return; |
||||
|
bottomPagination.setTotalPageCount(pageCount); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,374 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.inbox; |
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.graphics.Color; |
||||
|
import android.graphics.Typeface; |
||||
|
import android.graphics.drawable.Drawable; |
||||
|
import android.net.Uri; |
||||
|
import android.os.Build; |
||||
|
import android.os.Bundle; |
||||
|
import android.text.TextUtils; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.webkit.WebResourceRequest; |
||||
|
import android.webkit.WebView; |
||||
|
import android.webkit.WebViewClient; |
||||
|
import android.widget.ImageButton; |
||||
|
import android.widget.ImageView; |
||||
|
import android.widget.LinearLayout; |
||||
|
import android.widget.PopupWindow; |
||||
|
import android.widget.RelativeLayout; |
||||
|
import android.widget.TextView; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.appcompat.app.AlertDialog; |
||||
|
import androidx.appcompat.content.res.AppCompatResources; |
||||
|
import androidx.core.content.res.ResourcesCompat; |
||||
|
import androidx.lifecycle.ViewModelProviders; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.squareup.picasso.Picasso; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.activities.board.BoardActivity; |
||||
|
import gr.thmmy.mthmmy.activities.create_pm.CreatePMActivity; |
||||
|
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; |
||||
|
import gr.thmmy.mthmmy.activities.topic.TopicActivity; |
||||
|
import gr.thmmy.mthmmy.model.PM; |
||||
|
import gr.thmmy.mthmmy.model.ThmmyPage; |
||||
|
import gr.thmmy.mthmmy.utils.CircleTransform; |
||||
|
import gr.thmmy.mthmmy.utils.MessageAnimations; |
||||
|
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; |
||||
|
import gr.thmmy.mthmmy.viewmodel.InboxViewModel; |
||||
|
|
||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
||||
|
import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE; |
||||
|
import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL; |
||||
|
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL; |
||||
|
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL; |
||||
|
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME; |
||||
|
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; |
||||
|
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_WHITE; |
||||
|
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_YELLOW; |
||||
|
|
||||
|
public class InboxAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { |
||||
|
|
||||
|
private Context context; |
||||
|
private InboxViewModel inboxViewModel; |
||||
|
|
||||
|
public InboxAdapter(InboxActivity context) { |
||||
|
this.context = context; |
||||
|
inboxViewModel = ViewModelProviders.of(context).get(InboxViewModel.class); |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
||||
|
View itemView = LayoutInflater.from(parent.getContext()) |
||||
|
.inflate(R.layout.activity_inbox_pm_row, parent, false); |
||||
|
return new InboxAdapter.PMViewHolder(itemView); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { |
||||
|
InboxAdapter.PMViewHolder holder = (PMViewHolder) viewHolder; |
||||
|
PM currentPM = pms().get(position); |
||||
|
|
||||
|
//Post's WebView parameters
|
||||
|
holder.pm.setClickable(true); |
||||
|
holder.pm.setWebViewClient(new LinkLauncher()); |
||||
|
|
||||
|
Picasso.with(context) |
||||
|
.load(currentPM.getThumbnailUrl()) |
||||
|
.fit() |
||||
|
.centerCrop() |
||||
|
.error(Objects.requireNonNull(ResourcesCompat.getDrawable(context.getResources() |
||||
|
, R.drawable.ic_default_user_avatar_darker, null))) |
||||
|
.placeholder(Objects.requireNonNull(ResourcesCompat.getDrawable(context.getResources() |
||||
|
, R.drawable.ic_default_user_avatar_darker, null))) |
||||
|
.transform(new CircleTransform()) |
||||
|
.into(holder.thumbnail); |
||||
|
|
||||
|
//Sets username,submit date, index number, subject, post's and attached files texts
|
||||
|
holder.username.setText(currentPM.getAuthor()); |
||||
|
holder.pmDate.setText(currentPM.getPmDate()); |
||||
|
holder.subject.setText(currentPM.getSubject()); |
||||
|
holder.pm.loadDataWithBaseURL("file:///android_asset/", currentPM.getContent(), |
||||
|
"text/html", "UTF-8", null); |
||||
|
|
||||
|
// author info
|
||||
|
if (currentPM.getAuthorSpecialRank() != null && !currentPM.getAuthorSpecialRank().equals("")) { |
||||
|
holder.specialRank.setText(currentPM.getAuthorSpecialRank()); |
||||
|
holder.specialRank.setVisibility(View.VISIBLE); |
||||
|
} else holder.specialRank.setVisibility(View.GONE); |
||||
|
if (currentPM.getAuthorRank() != null && !currentPM.getAuthorRank().equals("")) { |
||||
|
holder.rank.setText(currentPM.getAuthorRank()); |
||||
|
holder.rank.setVisibility(View.VISIBLE); |
||||
|
} else holder.rank.setVisibility(View.GONE); |
||||
|
if (currentPM.getAuthorGender() != null && !currentPM.getAuthorGender().equals("")) { |
||||
|
holder.gender.setText(currentPM.getAuthorGender()); |
||||
|
holder.gender.setVisibility(View.VISIBLE); |
||||
|
} else holder.gender.setVisibility(View.GONE); |
||||
|
if (currentPM.getAuthorNumberOfPosts() != null && !currentPM.getAuthorNumberOfPosts().equals("")) { |
||||
|
holder.numberOfPosts.setText(currentPM.getAuthorNumberOfPosts()); |
||||
|
holder.numberOfPosts.setVisibility(View.VISIBLE); |
||||
|
} else holder.numberOfPosts.setVisibility(View.GONE); |
||||
|
if (currentPM.getAuthorPersonalText() != null && !currentPM.getAuthorPersonalText().equals("")) { |
||||
|
holder.personalText.setText(currentPM.getAuthorPersonalText()); |
||||
|
holder.personalText.setVisibility(View.VISIBLE); |
||||
|
} else holder.personalText.setVisibility(View.GONE); |
||||
|
if (currentPM.getAuthorColor() != USER_COLOR_YELLOW) |
||||
|
holder.username.setTextColor(currentPM.getAuthorColor()); |
||||
|
else holder.username.setTextColor(USER_COLOR_WHITE); |
||||
|
|
||||
|
if (currentPM.getAuthorNumberOfStars() > 0) { |
||||
|
holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets() |
||||
|
, "fonts/fontawesome-webfont.ttf")); |
||||
|
|
||||
|
String aStar = context.getResources().getString(R.string.fa_icon_star); |
||||
|
StringBuilder usersStars = new StringBuilder(); |
||||
|
for (int i = 0; i < currentPM.getAuthorNumberOfStars(); ++i) { |
||||
|
usersStars.append(aStar); |
||||
|
} |
||||
|
holder.stars.setText(usersStars.toString()); |
||||
|
holder.stars.setTextColor(currentPM.getAuthorColor()); |
||||
|
holder.stars.setVisibility(View.VISIBLE); |
||||
|
} else holder.stars.setVisibility(View.GONE); |
||||
|
|
||||
|
if (currentPM.isUserMentioned()) { |
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
||||
|
holder.cardChildLinear.setBackground(context.getResources(). |
||||
|
getDrawable(R.drawable.mention_card, null)); |
||||
|
} else |
||||
|
holder.cardChildLinear.setBackground(context.getResources(). |
||||
|
getDrawable(R.drawable.mention_card)); |
||||
|
} else if (currentPM.getAuthorColor() == ParseHelpers.USER_COLOR_PINK) { |
||||
|
//Special card for special member of the month!
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
||||
|
holder.cardChildLinear.setBackground(context.getResources(). |
||||
|
getDrawable(R.drawable.member_of_the_month_card, null)); |
||||
|
} else |
||||
|
holder.cardChildLinear.setBackground(context.getResources(). |
||||
|
getDrawable(R.drawable.member_of_the_month_card)); |
||||
|
} else holder.cardChildLinear.setBackground(null); |
||||
|
|
||||
|
//Avoid's view's visibility recycling
|
||||
|
if (inboxViewModel.isUserExtraInfoVisible(holder.getAdapterPosition())) { |
||||
|
holder.userExtraInfo.setVisibility(View.VISIBLE); |
||||
|
holder.userExtraInfo.setAlpha(1.0f); |
||||
|
|
||||
|
holder.username.setMaxLines(Integer.MAX_VALUE); |
||||
|
holder.username.setEllipsize(null); |
||||
|
|
||||
|
holder.subject.setTextColor(Color.parseColor("#FFFFFF")); |
||||
|
holder.subject.setMaxLines(Integer.MAX_VALUE); |
||||
|
holder.subject.setEllipsize(null); |
||||
|
} else { |
||||
|
holder.userExtraInfo.setVisibility(View.GONE); |
||||
|
holder.userExtraInfo.setAlpha(0.0f); |
||||
|
|
||||
|
holder.username.setMaxLines(1); |
||||
|
holder.username.setEllipsize(TextUtils.TruncateAt.END); |
||||
|
|
||||
|
holder.subject.setTextColor(Color.parseColor("#757575")); |
||||
|
holder.subject.setMaxLines(1); |
||||
|
holder.subject.setEllipsize(TextUtils.TruncateAt.END); |
||||
|
} |
||||
|
|
||||
|
//Sets graphics behavior
|
||||
|
holder.thumbnail.setOnClickListener(view -> { |
||||
|
//Clicking the thumbnail opens user's profile
|
||||
|
Intent intent = new Intent(context, ProfileActivity.class); |
||||
|
Bundle extras = new Bundle(); |
||||
|
extras.putString(BUNDLE_PROFILE_URL, currentPM.getAuthorProfileUrl()); |
||||
|
if (currentPM.getThumbnailUrl() == null) |
||||
|
extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); |
||||
|
else |
||||
|
extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, currentPM.getAuthorProfileUrl()); |
||||
|
extras.putString(BUNDLE_PROFILE_USERNAME, currentPM.getAuthor()); |
||||
|
intent.putExtras(extras); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
}); |
||||
|
holder.header.setOnClickListener(v -> { |
||||
|
//Clicking the header makes it expand/collapse
|
||||
|
inboxViewModel.toggleUserInfo(holder.getAdapterPosition()); |
||||
|
MessageAnimations.animateUserExtraInfoVisibility(holder.username, |
||||
|
holder.subject, Color.parseColor("#FFFFFF"), |
||||
|
Color.parseColor("#757575"), holder.userExtraInfo); |
||||
|
}); |
||||
|
//Clicking the expanded part of a header (the extra info) makes it collapse
|
||||
|
holder.userExtraInfo.setOnClickListener(v -> { |
||||
|
inboxViewModel.hideUserInfo(holder.getAdapterPosition()); |
||||
|
MessageAnimations.animateUserExtraInfoVisibility(holder.username, |
||||
|
holder.subject, Color.parseColor("#FFFFFF"), |
||||
|
Color.parseColor("#757575"), (LinearLayout) v); |
||||
|
}); |
||||
|
|
||||
|
holder.overflowButton.setOnClickListener(view -> { |
||||
|
LayoutInflater layoutInflater = LayoutInflater.from(context); |
||||
|
View popupContent = layoutInflater.inflate(R.layout.activity_inbox_overflow_menu, null); |
||||
|
|
||||
|
//Creates the PopupWindow
|
||||
|
final PopupWindow popUp = new PopupWindow(holder.overflowButton.getContext()); |
||||
|
popUp.setContentView(popupContent); |
||||
|
popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); |
||||
|
popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); |
||||
|
popUp.setFocusable(true); |
||||
|
|
||||
|
TextView quoteButton = popupContent.findViewById(R.id.pm_quote_button); |
||||
|
quoteButton.setVisibility(View.GONE); // TODO
|
||||
|
Drawable quoteDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_format_quote); |
||||
|
quoteButton.setCompoundDrawablesRelativeWithIntrinsicBounds(quoteDrawable, null, null, null); |
||||
|
quoteButton.setOnClickListener(v -> { |
||||
|
Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show(); |
||||
|
// TODO: Create delete PM task
|
||||
|
}); |
||||
|
|
||||
|
final TextView replyButton = popupContent.findViewById(R.id.pm_reply_button); |
||||
|
Drawable replyDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_reply); |
||||
|
replyButton.setCompoundDrawablesRelativeWithIntrinsicBounds(replyDrawable, null, null, null); |
||||
|
|
||||
|
replyButton.setOnClickListener(v -> { |
||||
|
Intent sendPMIntent = new Intent(context, CreatePMActivity.class); |
||||
|
sendPMIntent.putExtra(CreatePMActivity.BUNDLE_SEND_PM_URL, currentPM.getReplyUrl()); |
||||
|
context.startActivity(sendPMIntent); |
||||
|
popUp.dismiss(); |
||||
|
}); |
||||
|
|
||||
|
TextView deletePostButton = popupContent.findViewById(R.id.delete_post); |
||||
|
|
||||
|
Drawable deleteStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_white_24dp); |
||||
|
deletePostButton.setVisibility(View.GONE); //TODO
|
||||
|
deletePostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(deleteStartDrawable, null, null, null); |
||||
|
popupContent.findViewById(R.id.delete_post).setOnClickListener(v -> { |
||||
|
new AlertDialog.Builder(holder.overflowButton.getContext()) |
||||
|
.setTitle("Delete personal message") |
||||
|
.setMessage("Do you really want to delete this personal message?") |
||||
|
.setIcon(android.R.drawable.ic_dialog_alert) |
||||
|
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> { |
||||
|
Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show(); |
||||
|
// TODO: Create delete PM task
|
||||
|
}) |
||||
|
.setNegativeButton(android.R.string.no, null).show(); |
||||
|
popUp.dismiss(); |
||||
|
}); |
||||
|
|
||||
|
//Displays the popup
|
||||
|
popUp.showAsDropDown(holder.overflowButton); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemCount() { |
||||
|
return inboxViewModel.getInbox() == null ? 0 : pms().size(); |
||||
|
} |
||||
|
|
||||
|
private ArrayList<PM> pms() { |
||||
|
return inboxViewModel.getInbox().getPms(); |
||||
|
} |
||||
|
|
||||
|
static class PMViewHolder extends RecyclerView.ViewHolder { |
||||
|
final LinearLayout cardChildLinear; |
||||
|
final TextView pmDate, username, subject; |
||||
|
final ImageView thumbnail; |
||||
|
final public WebView pm; |
||||
|
final ImageButton overflowButton; |
||||
|
final RelativeLayout header; |
||||
|
final LinearLayout userExtraInfo; |
||||
|
|
||||
|
final TextView specialRank, rank, gender, numberOfPosts, personalText, stars; |
||||
|
|
||||
|
PMViewHolder(@NonNull View view) { |
||||
|
super(view); |
||||
|
cardChildLinear = view.findViewById(R.id.card_child_linear); |
||||
|
pmDate = view.findViewById(R.id.pm_date); |
||||
|
thumbnail = view.findViewById(R.id.thumbnail); |
||||
|
username = view.findViewById(R.id.username); |
||||
|
subject = view.findViewById(R.id.subject); |
||||
|
pm = view.findViewById(R.id.pm); |
||||
|
pm.setBackgroundColor(Color.argb(1, 255, 255, 255)); |
||||
|
overflowButton = view.findViewById(R.id.pm_overflow_menu); |
||||
|
|
||||
|
//User's extra info
|
||||
|
header = view.findViewById(R.id.header); |
||||
|
userExtraInfo = view.findViewById(R.id.user_extra_info); |
||||
|
specialRank = view.findViewById(R.id.special_rank); |
||||
|
rank = view.findViewById(R.id.rank); |
||||
|
gender = view.findViewById(R.id.gender); |
||||
|
numberOfPosts = view.findViewById(R.id.number_of_posts); |
||||
|
personalText = view.findViewById(R.id.personal_text); |
||||
|
stars = view.findViewById(R.id.stars); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* This class is used to handle link clicks in WebViews. When link url is one that the app can |
||||
|
* handle internally, it does. Otherwise user is prompt to open the link in a browser. |
||||
|
*/ |
||||
|
private class LinkLauncher extends WebViewClient { |
||||
|
@Override |
||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) { |
||||
|
final Uri uri = Uri.parse(url); |
||||
|
return handleUri(uri); |
||||
|
} |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
@Override |
||||
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { |
||||
|
final Uri uri = request.getUrl(); |
||||
|
return handleUri(uri); |
||||
|
} |
||||
|
|
||||
|
@SuppressWarnings("SameReturnValue") |
||||
|
private boolean handleUri(final Uri uri) { |
||||
|
final String uriString = uri.toString(); |
||||
|
|
||||
|
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(uri); |
||||
|
if (target.is(ThmmyPage.PageCategory.TOPIC)) { |
||||
|
//This url points to a topic
|
||||
|
Intent intent = new Intent(context, TopicActivity.class); |
||||
|
Bundle extras = new Bundle(); |
||||
|
extras.putString(BUNDLE_TOPIC_URL, uriString); |
||||
|
intent.putExtras(extras); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
return true; |
||||
|
} else if (target.is(ThmmyPage.PageCategory.BOARD)) { |
||||
|
Intent intent = new Intent(context, BoardActivity.class); |
||||
|
Bundle extras = new Bundle(); |
||||
|
extras.putString(BUNDLE_BOARD_URL, uriString); |
||||
|
extras.putString(BUNDLE_BOARD_TITLE, ""); |
||||
|
intent.putExtras(extras); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
return true; |
||||
|
} else if (target.is(ThmmyPage.PageCategory.PROFILE)) { |
||||
|
Intent intent = new Intent(context, ProfileActivity.class); |
||||
|
Bundle extras = new Bundle(); |
||||
|
extras.putString(BUNDLE_PROFILE_URL, uriString); |
||||
|
extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); |
||||
|
extras.putString(BUNDLE_PROFILE_USERNAME, ""); |
||||
|
intent.putExtras(extras); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, uri); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
|
||||
|
//Method always returns true as no url should be loaded in the WebViews
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,203 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.inbox.tasks; |
||||
|
|
||||
|
import org.jsoup.Jsoup; |
||||
|
import org.jsoup.nodes.Document; |
||||
|
import org.jsoup.nodes.Element; |
||||
|
import org.jsoup.select.Elements; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.Objects; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.base.BaseActivity; |
||||
|
import gr.thmmy.mthmmy.model.Inbox; |
||||
|
import gr.thmmy.mthmmy.model.PM; |
||||
|
import gr.thmmy.mthmmy.utils.NetworkResultCodes; |
||||
|
import gr.thmmy.mthmmy.utils.parsing.NewParseTask; |
||||
|
import gr.thmmy.mthmmy.utils.parsing.ParseException; |
||||
|
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; |
||||
|
import okhttp3.Response; |
||||
|
|
||||
|
public class InboxTask extends NewParseTask<Inbox> { |
||||
|
@Override |
||||
|
protected Inbox parse(Document document, Response response) throws ParseException { |
||||
|
Inbox inbox = new Inbox(); |
||||
|
ParseHelpers.deobfuscateElements(document.select("span.__cf_email__,a.__cf_email__"), true); |
||||
|
|
||||
|
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document); |
||||
|
|
||||
|
inbox.setCurrentPageIndex(ParseHelpers.parseCurrentPageIndexInbox(document, language)); |
||||
|
inbox.setNumberOfPages(ParseHelpers.parseNumberOfPagesInbox(document, inbox.getCurrentPageIndex(), language)); |
||||
|
|
||||
|
ArrayList<PM> pmList = parsePMs(document, language); |
||||
|
inbox.setPms(pmList); |
||||
|
return inbox; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected int getResultCode(Response response, Inbox data) { |
||||
|
return NetworkResultCodes.SUCCESSFUL; |
||||
|
} |
||||
|
|
||||
|
private ArrayList<PM> parsePMs(Document document, ParseHelpers.Language language) { |
||||
|
ArrayList<PM> pms = new ArrayList<>(); |
||||
|
Elements pmContainerContainers = document.select("td[style=padding: 1px 1px 0 1px;]"); |
||||
|
for (Element pmContainerContainer : pmContainerContainers) { |
||||
|
PM pm = new PM(); |
||||
|
boolean isAuthorDeleted; |
||||
|
Element pmContainer = pmContainerContainer.select("table[style=table-layout: fixed;]").first().child(0); |
||||
|
|
||||
|
Element thumbnail = pmContainer.select("img.avatar").first(); |
||||
|
// User might not have an avatar
|
||||
|
if (thumbnail != null) |
||||
|
pm.setThumbnailUrl(thumbnail.attr("src")); |
||||
|
|
||||
|
Element subjectAndDateContainer = pmContainer.select("td[align=left]").first(); |
||||
|
pm.setSubject(subjectAndDateContainer.select("b").first().text()); |
||||
|
Element dateContainer = subjectAndDateContainer.select("div").first(); |
||||
|
pm.setPmDate(subjectAndDateContainer.select("div").first().text()); |
||||
|
|
||||
|
String content = ParseHelpers.youtubeEmbeddedFix(pmContainer.select("div.personalmessage").first()); |
||||
|
//Adds stuff to make it work in WebView
|
||||
|
//style.css
|
||||
|
content = "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + content; |
||||
|
pm.setContent(content); |
||||
|
|
||||
|
pm.setQuoteUrl(pmContainer.select("img[src=https://www.thmmy.gr/smf/Themes/scribbles2_114/images/buttons/quote.gif]") |
||||
|
.first().parent().attr("href")); |
||||
|
pm.setReplyUrl(pmContainer.select("img[src=https://www.thmmy.gr/smf/Themes/scribbles2_114/images/buttons/im_reply.gif]") |
||||
|
.first().parent().attr("href")); |
||||
|
pm.setDeleteUrl(pmContainer.select("img[src=https://www.thmmy.gr/smf/Themes/scribbles2_114/images/buttons/delete.gif]") |
||||
|
.first().parent().attr("href")); |
||||
|
|
||||
|
// language specific parsing
|
||||
|
Element username; |
||||
|
if (language == ParseHelpers.Language.GREEK) { |
||||
|
//Finds username and profile's url
|
||||
|
username = pmContainer.select("a[title^=Εμφάνιση προφίλ του μέλους]").first(); |
||||
|
if (username == null) { //Deleted profile
|
||||
|
isAuthorDeleted = true; |
||||
|
String authorName = pmContainer.select("td:has(div.smalltext:containsOwn(Επισκέπτης))[style^=overflow]") |
||||
|
.first().text(); |
||||
|
authorName = authorName.substring(0, authorName.indexOf(" Επισκέπτης")); |
||||
|
pm.setAuthor(authorName); |
||||
|
pm.setAuthorColor(ParseHelpers.USER_COLOR_YELLOW); |
||||
|
} else { |
||||
|
isAuthorDeleted = false; |
||||
|
pm.setAuthor(username.html()); |
||||
|
pm.setAuthorProfileUrl(username.attr("href")); |
||||
|
} |
||||
|
|
||||
|
String date = dateContainer.text(); |
||||
|
date = date.substring(date.indexOf("στις:") + 6, date.indexOf(" »")); |
||||
|
pm.setPmDate(date); |
||||
|
} else { |
||||
|
//Finds username
|
||||
|
username = pmContainer.select("a[title^=View the profile of]").first(); |
||||
|
if (username == null) { //Deleted profile
|
||||
|
isAuthorDeleted = true; |
||||
|
String authorName = pmContainer |
||||
|
.select("td:has(div.smalltext:containsOwn(Guest))[style^=overflow]") |
||||
|
.first().text(); |
||||
|
authorName = authorName.substring(0, authorName.indexOf(" Guest")); |
||||
|
pm.setAuthor(authorName); |
||||
|
pm.setAuthorColor(ParseHelpers.USER_COLOR_YELLOW); |
||||
|
} else { |
||||
|
isAuthorDeleted = false; |
||||
|
pm.setAuthor(username.html()); |
||||
|
pm.setAuthorProfileUrl(username.attr("href")); |
||||
|
} |
||||
|
|
||||
|
String date = dateContainer.text(); |
||||
|
date = date.substring(date.indexOf("on:") + 4, date.indexOf(" »")); |
||||
|
pm.setPmDate(date); |
||||
|
} |
||||
|
|
||||
|
if (!isAuthorDeleted) { |
||||
|
int postsLineIndex = -1; |
||||
|
int starsLineIndex = -1; |
||||
|
|
||||
|
Element authorInfoContainer = pmContainer.select("div.smalltext").first(); |
||||
|
List<String> infoList = Arrays.asList(authorInfoContainer.html().split("<br>")); |
||||
|
|
||||
|
if (language == ParseHelpers.Language.GREEK) { |
||||
|
for (String line : infoList) { |
||||
|
if (line.contains("Μηνύματα:")) { |
||||
|
postsLineIndex = infoList.indexOf(line); |
||||
|
//Remove any line breaks and spaces on the start and end
|
||||
|
pm.setAuthorNumberOfPosts(line.replace("\n", "").replace("\r", "").trim()); |
||||
|
} |
||||
|
if (line.contains("Φύλο:")) { |
||||
|
if (line.contains("alt=\"Άντρας\"")) |
||||
|
pm.setAuthorGender("Φύλο: Άντρας"); |
||||
|
else |
||||
|
pm.setAuthorGender("Φύλο: Γυναίκα"); |
||||
|
} |
||||
|
if (line.contains("alt=\"*\"")) { |
||||
|
starsLineIndex = infoList.indexOf(line); |
||||
|
Document starsHtml = Jsoup.parse(line); |
||||
|
pm.setAuthorNumberOfStars(starsHtml.select("img[alt]").size()); |
||||
|
pm.setAuthorColor(ParseHelpers.colorPicker(starsHtml.select("img[alt]").first() |
||||
|
.attr("abs:src"))); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
for (String line : infoList) { |
||||
|
if (line.contains("Posts:")) { |
||||
|
postsLineIndex = infoList.indexOf(line); |
||||
|
//Remove any line breaks and spaces on the start and end
|
||||
|
pm.setAuthorNumberOfPosts(line.replace("\n", "").replace("\r", "").trim()); |
||||
|
} |
||||
|
if (line.contains("Gender:")) { |
||||
|
if (line.contains("alt=\"Male\"")) |
||||
|
pm.setAuthorGender("Gender: Male"); |
||||
|
else |
||||
|
pm.setAuthorGender("Gender: Female"); |
||||
|
} |
||||
|
if (line.contains("alt=\"*\"")) { |
||||
|
starsLineIndex = infoList.indexOf(line); |
||||
|
Document starsHtml = Jsoup.parse(line); |
||||
|
pm.setAuthorNumberOfStars(starsHtml.select("img[alt]").size()); |
||||
|
pm.setAuthorColor(ParseHelpers.colorPicker(starsHtml.select("img[alt]").first() |
||||
|
.attr("abs:src"))); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//If this member has no stars yet ==> New member,
|
||||
|
//or is just a member
|
||||
|
if (starsLineIndex == -1 || starsLineIndex == 1) { |
||||
|
pm.setAuthorRank(infoList.get(0).trim()); //First line has the rank
|
||||
|
//They don't have a special rank
|
||||
|
} else if (starsLineIndex == 2) { //This member has a special rank
|
||||
|
pm.setAuthorSpecialRank(infoList.get(0).trim());//First line has the special rank
|
||||
|
pm.setAuthorRank(infoList.get(1).trim());//Second line has the rank
|
||||
|
} |
||||
|
for (int i = postsLineIndex + 1; i < infoList.size() - 1; ++i) { |
||||
|
//Searches under "Posts:"
|
||||
|
//and above "Personal Message", "View Profile" etc buttons
|
||||
|
String thisLine = infoList.get(i); |
||||
|
if (!Objects.equals(thisLine, "") && thisLine != null |
||||
|
&& !Objects.equals(thisLine, " \n") |
||||
|
&& !thisLine.contains("avatar") |
||||
|
&& !thisLine.contains("<a href=")) { |
||||
|
String personalText = thisLine; |
||||
|
personalText = personalText.replace("\n", "").replace("\r", "").trim(); |
||||
|
pm.setAuthorPersonalText(personalText); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Checks post for mentions of this user (if the user is logged in)
|
||||
|
if (BaseActivity.getSessionManager().isLoggedIn() && |
||||
|
ParseHelpers.mentionsPattern.matcher(pm.getContent()).find()) { |
||||
|
pm.setUserMentioned(true); |
||||
|
} |
||||
|
} |
||||
|
pms.add(pm); |
||||
|
} |
||||
|
return pms; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,32 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
public class Inbox { |
||||
|
private ArrayList<PM> pms; |
||||
|
private int currentPageIndex, numberOfPages; |
||||
|
|
||||
|
public int getCurrentPageIndex() { |
||||
|
return currentPageIndex; |
||||
|
} |
||||
|
|
||||
|
public void setCurrentPageIndex(int currentPageIndex) { |
||||
|
this.currentPageIndex = currentPageIndex; |
||||
|
} |
||||
|
|
||||
|
public int getNumberOfPages() { |
||||
|
return numberOfPages; |
||||
|
} |
||||
|
|
||||
|
public void setNumberOfPages(int numberOfPages) { |
||||
|
this.numberOfPages = numberOfPages; |
||||
|
} |
||||
|
|
||||
|
public ArrayList<PM> getPms() { |
||||
|
return pms; |
||||
|
} |
||||
|
|
||||
|
public void setPms(ArrayList<PM> pms) { |
||||
|
this.pms = pms; |
||||
|
} |
||||
|
} |
@ -0,0 +1,157 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.utils.parsing.StringUtils; |
||||
|
import gr.thmmy.mthmmy.utils.parsing.ThmmyDateTimeParser; |
||||
|
|
||||
|
public class PM { |
||||
|
|
||||
|
private String thumbnailUrl; |
||||
|
private String author; |
||||
|
private String authorProfileUrl; |
||||
|
private String subject; |
||||
|
private String content; |
||||
|
private String pmDate; |
||||
|
private String deleteUrl, replyUrl, quoteUrl; |
||||
|
private int authorColor; |
||||
|
private String authorGender; |
||||
|
private String authorNumberOfPosts; |
||||
|
private String authorRank, authorSpecialRank, authorPersonalText; |
||||
|
private int authorNumberOfStars; |
||||
|
private boolean isUserMentioned; |
||||
|
|
||||
|
public int getAuthorColor() { |
||||
|
return authorColor; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorPersonalText() { |
||||
|
return authorPersonalText; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorPersonalText(String authorPersonalText) { |
||||
|
this.authorPersonalText = authorPersonalText; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorNumberOfPosts() { |
||||
|
return authorNumberOfPosts; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorRank() { |
||||
|
return authorRank; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorRank(String rank) { |
||||
|
this.authorRank = rank; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorSpecialRank() { |
||||
|
return authorSpecialRank; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorSpecialRank(String authorSpecialRank) { |
||||
|
this.authorSpecialRank = authorSpecialRank; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorGender() { |
||||
|
return authorGender; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorGender(String authorGender) { |
||||
|
this.authorGender = authorGender; |
||||
|
} |
||||
|
|
||||
|
public int getAuthorNumberOfStars() { |
||||
|
return authorNumberOfStars; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorNumberOfStars(int authorNumberOfStars) { |
||||
|
this.authorNumberOfStars = authorNumberOfStars; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorNumberOfPosts(String authorNumberOfPosts) { |
||||
|
this.authorNumberOfPosts = authorNumberOfPosts; |
||||
|
} |
||||
|
|
||||
|
public String getReplyUrl() { |
||||
|
return replyUrl; |
||||
|
} |
||||
|
|
||||
|
public void setReplyUrl(String replyUrl) { |
||||
|
this.replyUrl = replyUrl; |
||||
|
} |
||||
|
|
||||
|
public String getQuoteUrl() { |
||||
|
return quoteUrl; |
||||
|
} |
||||
|
|
||||
|
public void setQuoteUrl(String quoteUrl) { |
||||
|
this.quoteUrl = quoteUrl; |
||||
|
} |
||||
|
|
||||
|
public String getDeleteUrl() { |
||||
|
return deleteUrl; |
||||
|
} |
||||
|
|
||||
|
public void setDeleteUrl(String deleteUrl) { |
||||
|
this.deleteUrl = deleteUrl; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorColor(int authorColor) { |
||||
|
this.authorColor = authorColor; |
||||
|
} |
||||
|
|
||||
|
public boolean isUserMentioned() { |
||||
|
return isUserMentioned; |
||||
|
} |
||||
|
|
||||
|
public void setUserMentioned(boolean userMentioned) { |
||||
|
isUserMentioned = userMentioned; |
||||
|
} |
||||
|
|
||||
|
public String getAuthorProfileUrl() { |
||||
|
return authorProfileUrl; |
||||
|
} |
||||
|
|
||||
|
public void setAuthorProfileUrl(String authorProfileUrl) { |
||||
|
this.authorProfileUrl = authorProfileUrl; |
||||
|
} |
||||
|
|
||||
|
public void setThumbnailUrl(String thumbnailUrl) { |
||||
|
this.thumbnailUrl = thumbnailUrl; |
||||
|
} |
||||
|
|
||||
|
public void setAuthor(String author) { |
||||
|
this.author = author; |
||||
|
} |
||||
|
|
||||
|
public void setSubject(String subject) { |
||||
|
this.subject = subject; |
||||
|
} |
||||
|
|
||||
|
public void setContent(String content) { |
||||
|
this.content = content; |
||||
|
} |
||||
|
|
||||
|
public void setPmDate(String pmDate) { |
||||
|
this.pmDate = pmDate; |
||||
|
} |
||||
|
|
||||
|
public String getAuthor() { |
||||
|
return author; |
||||
|
} |
||||
|
|
||||
|
public String getContent() { |
||||
|
return content; |
||||
|
} |
||||
|
|
||||
|
public String getSubject() { |
||||
|
return subject; |
||||
|
} |
||||
|
|
||||
|
public String getThumbnailUrl() { |
||||
|
return thumbnailUrl; |
||||
|
} |
||||
|
|
||||
|
public String getPmDate() { |
||||
|
return pmDate; |
||||
|
} |
||||
|
} |
@ -0,0 +1,263 @@ |
|||||
|
package gr.thmmy.mthmmy.pagination; |
||||
|
|
||||
|
import android.annotation.SuppressLint; |
||||
|
import android.content.Context; |
||||
|
import android.graphics.Rect; |
||||
|
import android.os.Handler; |
||||
|
import android.util.AttributeSet; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.MotionEvent; |
||||
|
import android.view.View; |
||||
|
import android.widget.ImageButton; |
||||
|
import android.widget.LinearLayout; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.annotation.Nullable; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
|
||||
|
public class BottomPaginationView extends LinearLayout { |
||||
|
|
||||
|
/** |
||||
|
* Holds the initial time delay before a click on bottom navigation bar is considered long |
||||
|
*/ |
||||
|
private final long INITIAL_DELAY = 500; |
||||
|
private boolean autoIncrement = false; |
||||
|
private boolean autoDecrement = false; |
||||
|
/** |
||||
|
* Holds the number of pages to be added or subtracted from current page on each step while a |
||||
|
* long click is held in either next or previous buttons |
||||
|
*/ |
||||
|
private static final int SMALL_STEP = 1; |
||||
|
/** |
||||
|
* Holds the number of pages to be added or subtracted from current page on each step while a |
||||
|
* long click is held in either first or last buttons |
||||
|
*/ |
||||
|
private static final int LARGE_STEP = 10; |
||||
|
/** |
||||
|
* Used for handling bottom navigation bar's buttons long click user interactions |
||||
|
*/ |
||||
|
private final Handler repeatUpdateHandler = new Handler(); |
||||
|
|
||||
|
private ImageButton firstPage, previousPage, nextPage, lastPage; |
||||
|
private TextView pageIndicator; |
||||
|
|
||||
|
private int onDownPageIndex, indicatorPageIndex, totalPageCount; |
||||
|
|
||||
|
private OnPageRequestedListener onPageRequestedListener; |
||||
|
|
||||
|
public interface OnPageRequestedListener { |
||||
|
void onPageRequested(int index); |
||||
|
} |
||||
|
|
||||
|
public BottomPaginationView(Context context) { |
||||
|
this(context, null); |
||||
|
} |
||||
|
|
||||
|
public BottomPaginationView(Context context, @Nullable AttributeSet attrs) { |
||||
|
super(context, attrs); |
||||
|
|
||||
|
LayoutInflater inflater = LayoutInflater.from(context); |
||||
|
inflater.inflate(R.layout.pagination, this, true); |
||||
|
|
||||
|
firstPage = findViewById(R.id.page_first_button); |
||||
|
previousPage = findViewById(R.id.page_previous_button); |
||||
|
pageIndicator = findViewById(R.id.page_indicator); |
||||
|
nextPage = findViewById(R.id.page_next_button); |
||||
|
lastPage = findViewById(R.id.page_last_button); |
||||
|
|
||||
|
initDecrementButton(firstPage, LARGE_STEP); |
||||
|
initDecrementButton(previousPage, SMALL_STEP); |
||||
|
initIncrementButton(nextPage, SMALL_STEP); |
||||
|
initIncrementButton(lastPage, LARGE_STEP); |
||||
|
} |
||||
|
|
||||
|
public void setOnPageRequestedListener(OnPageRequestedListener onPageRequestedListener) { |
||||
|
this.onPageRequestedListener = onPageRequestedListener; |
||||
|
} |
||||
|
|
||||
|
public boolean setIndicatedPageIndex(int index) { |
||||
|
if (index != indicatorPageIndex) { |
||||
|
this.indicatorPageIndex = index; |
||||
|
updateUI(); |
||||
|
return true; |
||||
|
} else return false; |
||||
|
} |
||||
|
|
||||
|
public void setTotalPageCount(int totalPageCount) { |
||||
|
this.totalPageCount = totalPageCount; |
||||
|
updateUI(); |
||||
|
} |
||||
|
|
||||
|
public void updateUI() { |
||||
|
pageIndicator.setText(indicatorPageIndex + "/" + totalPageCount); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue |
||||
|
* of page value when long pressing one of the page navigation buttons. |
||||
|
*/ |
||||
|
private class RepetitiveUpdater implements Runnable { |
||||
|
private final int step; |
||||
|
|
||||
|
/** |
||||
|
* @param step number of pages to add/subtract on each repetition |
||||
|
*/ |
||||
|
RepetitiveUpdater(int step) { |
||||
|
this.step = step; |
||||
|
} |
||||
|
|
||||
|
public void run() { |
||||
|
long REPEAT_DELAY = 250; |
||||
|
if (autoIncrement) { |
||||
|
incrementPageIndicator(step); |
||||
|
repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), REPEAT_DELAY); |
||||
|
} else if (autoDecrement) { |
||||
|
decrementPageIndicator(step); |
||||
|
repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), REPEAT_DELAY); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean incrementPageIndicator(int step) { |
||||
|
int oldIndicatorIndex = indicatorPageIndex; |
||||
|
if (oldIndicatorIndex <= totalPageCount - step) |
||||
|
setIndicatedPageIndex(indicatorPageIndex + step); |
||||
|
else |
||||
|
setIndicatedPageIndex(totalPageCount); |
||||
|
return oldIndicatorIndex != indicatorPageIndex; |
||||
|
} |
||||
|
|
||||
|
public boolean decrementPageIndicator(int step) { |
||||
|
int oldIndicatorIndex = indicatorPageIndex; |
||||
|
if (oldIndicatorIndex > step) |
||||
|
setIndicatedPageIndex(indicatorPageIndex - step); |
||||
|
else |
||||
|
setIndicatedPageIndex(1); |
||||
|
return oldIndicatorIndex != indicatorPageIndex; |
||||
|
} |
||||
|
|
||||
|
@SuppressLint("ClickableViewAccessibility") |
||||
|
private void initIncrementButton(ImageButton increment, final int step) { |
||||
|
// Increment once for a click
|
||||
|
increment.setOnClickListener(v -> { |
||||
|
if (!autoIncrement && step == LARGE_STEP) { |
||||
|
boolean indicatorChanged = setIndicatedPageIndex(totalPageCount); |
||||
|
if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} else if (!autoIncrement) { |
||||
|
boolean indicatorChanged = incrementPageIndicator(1); |
||||
|
if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Auto increment for a long click
|
||||
|
increment.setOnLongClickListener( |
||||
|
arg0 -> { |
||||
|
paginationDisable(arg0); |
||||
|
autoIncrement = true; |
||||
|
repeatUpdateHandler.postDelayed(new BottomPaginationView.RepetitiveUpdater(step), INITIAL_DELAY); |
||||
|
return false; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
// When the button is released
|
||||
|
increment.setOnTouchListener(new View.OnTouchListener() { |
||||
|
private Rect rect; |
||||
|
|
||||
|
public boolean onTouch(View v, MotionEvent event) { |
||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) { |
||||
|
onDownPageIndex = indicatorPageIndex; |
||||
|
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); |
||||
|
} else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) { |
||||
|
autoIncrement = false; |
||||
|
paginationEnabled(true); |
||||
|
onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) { |
||||
|
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { |
||||
|
autoIncrement = false; |
||||
|
setIndicatedPageIndex(onDownPageIndex); |
||||
|
paginationEnabled(true); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@SuppressLint("ClickableViewAccessibility") |
||||
|
private void initDecrementButton(ImageButton decrement, final int step) { |
||||
|
// Decrement once for a click
|
||||
|
decrement.setOnClickListener(v -> { |
||||
|
if (!autoDecrement && step == LARGE_STEP) { |
||||
|
boolean indicatorChanged = setIndicatedPageIndex(1); |
||||
|
if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} else if (!autoDecrement) { |
||||
|
boolean indicatorChanged = decrementPageIndicator(1); |
||||
|
if (indicatorChanged) onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// Auto decrement for a long click
|
||||
|
decrement.setOnLongClickListener( |
||||
|
arg0 -> { |
||||
|
paginationDisable(arg0); |
||||
|
autoDecrement = true; |
||||
|
repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY); |
||||
|
return false; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
// When the button is released
|
||||
|
decrement.setOnTouchListener(new View.OnTouchListener() { |
||||
|
private Rect rect; |
||||
|
|
||||
|
public boolean onTouch(View v, MotionEvent event) { |
||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) { |
||||
|
onDownPageIndex = indicatorPageIndex; |
||||
|
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); |
||||
|
} else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) { |
||||
|
autoDecrement = false; |
||||
|
paginationEnabled(true); |
||||
|
onPageRequestedListener.onPageRequested(indicatorPageIndex); |
||||
|
} else if (event.getAction() == MotionEvent.ACTION_MOVE) { |
||||
|
if (rect != null && |
||||
|
!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) { |
||||
|
autoIncrement = false; |
||||
|
setIndicatedPageIndex(onDownPageIndex); |
||||
|
paginationEnabled(true); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public void paginationDisable(View exception) { |
||||
|
if (exception == firstPage) { |
||||
|
previousPage.setEnabled(false); |
||||
|
nextPage.setEnabled(false); |
||||
|
lastPage.setEnabled(false); |
||||
|
} else if (exception == previousPage) { |
||||
|
firstPage.setEnabled(false); |
||||
|
nextPage.setEnabled(false); |
||||
|
lastPage.setEnabled(false); |
||||
|
} else if (exception == nextPage) { |
||||
|
firstPage.setEnabled(false); |
||||
|
previousPage.setEnabled(false); |
||||
|
lastPage.setEnabled(false); |
||||
|
} else if (exception == lastPage) { |
||||
|
firstPage.setEnabled(false); |
||||
|
previousPage.setEnabled(false); |
||||
|
nextPage.setEnabled(false); |
||||
|
} else { |
||||
|
paginationEnabled(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void paginationEnabled(boolean enabled) { |
||||
|
firstPage.setEnabled(enabled); |
||||
|
previousPage.setEnabled(enabled); |
||||
|
nextPage.setEnabled(enabled); |
||||
|
lastPage.setEnabled(enabled); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package gr.thmmy.mthmmy.utils.parsing; |
||||
|
|
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
public class StringUtils { |
||||
|
/** |
||||
|
* Method that extracts the base URL from a topic's page URL. For example a topic with url similar to |
||||
|
* "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or |
||||
|
* "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1" |
||||
|
* has the base url "https://www.thmmy.gr/smf/index.php?topic=1" |
||||
|
* |
||||
|
* @param topicURL a topic's page URL |
||||
|
* @return the base URL of the given topic |
||||
|
*/ |
||||
|
public static String getBaseURL(String topicURL) { |
||||
|
String forumUrl = "https://www.thmmy.gr/smf/index.php?"; |
||||
|
Matcher baseUrlMatcher = Pattern.compile("topic=[0-9]+").matcher(topicURL); |
||||
|
if (baseUrlMatcher.find()) |
||||
|
return forumUrl + topicURL.substring(baseUrlMatcher.start(), baseUrlMatcher.end()); |
||||
|
else return ""; |
||||
|
} |
||||
|
|
||||
|
public static int extractUserCodeFromUrl(String url) { |
||||
|
Matcher userCodeMatcher = Pattern.compile("u=[0-9]+").matcher(url); |
||||
|
if (userCodeMatcher.find()) |
||||
|
return Integer.parseInt(url.substring(userCodeMatcher.start()+2, userCodeMatcher.end())); |
||||
|
else return -1; |
||||
|
} |
||||
|
} |
@ -0,0 +1,116 @@ |
|||||
|
package gr.thmmy.mthmmy.viewmodel; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
|
||||
|
import androidx.lifecycle.MutableLiveData; |
||||
|
import androidx.lifecycle.ViewModel; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.activities.inbox.tasks.InboxTask; |
||||
|
import gr.thmmy.mthmmy.model.Inbox; |
||||
|
import gr.thmmy.mthmmy.model.PM; |
||||
|
import gr.thmmy.mthmmy.pagination.BottomPaginationView; |
||||
|
import gr.thmmy.mthmmy.utils.ExternalAsyncTask; |
||||
|
import gr.thmmy.mthmmy.utils.NetworkResultCodes; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
public class InboxViewModel extends ViewModel implements InboxTask.OnNetworkTaskFinishedListener<Inbox>, |
||||
|
BottomPaginationView.OnPageRequestedListener { |
||||
|
private static final String INBOX_URL = "https://www.thmmy.gr/smf/index.php?action=pm"; |
||||
|
/** |
||||
|
* caches the expand/collapse state of the user extra info in the current page for the recyclerview |
||||
|
*/ |
||||
|
private ArrayList<Boolean> userExtraInfoVisibile = new ArrayList<>(); |
||||
|
|
||||
|
private MutableLiveData<Integer> pageIndicatorIndex = new MutableLiveData<>(); |
||||
|
private MutableLiveData<Integer> pageCount = new MutableLiveData<>(); |
||||
|
|
||||
|
private InboxTask currentInboxTask; |
||||
|
|
||||
|
private Inbox inbox; |
||||
|
private InboxTask.OnNetworkTaskFinishedListener<Inbox> onInboxTaskFinishedListener; |
||||
|
private InboxTask.OnTaskStartedListener onInboxTaskStartedListener; |
||||
|
private InboxTask.OnTaskCancelledListener onInboxTaskCancelledListener; |
||||
|
|
||||
|
public void loadInbox() { |
||||
|
loadUrl(INBOX_URL); |
||||
|
} |
||||
|
|
||||
|
public void loadInboxPage(int index) { |
||||
|
loadUrl(INBOX_URL + ";f=inbox;sort=date;start=" + 15*(index-1)); |
||||
|
} |
||||
|
|
||||
|
public void loadUrl(String url) { |
||||
|
stopLoading(); |
||||
|
currentInboxTask = new InboxTask(); |
||||
|
currentInboxTask.setOnTaskStartedListener(onInboxTaskStartedListener); |
||||
|
currentInboxTask.setOnNetworkTaskFinishedListener(this); |
||||
|
currentInboxTask.setOnTaskCancelledListener(onInboxTaskCancelledListener); |
||||
|
currentInboxTask.execute(url); |
||||
|
} |
||||
|
|
||||
|
public void stopLoading() { |
||||
|
if (currentInboxTask != null && currentInboxTask.getStatus() == AsyncTask.Status.RUNNING) { |
||||
|
Timber.i("Canceling inbox task"); |
||||
|
currentInboxTask.cancel(true); |
||||
|
onInboxTaskCancelledListener.onTaskCanceled(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void setOnInboxTaskFinishedListener(InboxTask.OnNetworkTaskFinishedListener<Inbox> onInboxTaskFinishedListener) { |
||||
|
this.onInboxTaskFinishedListener = onInboxTaskFinishedListener; |
||||
|
} |
||||
|
|
||||
|
public void setOnInboxTaskStartedListener(InboxTask.OnTaskStartedListener onInboxTaskStartedListener) { |
||||
|
this.onInboxTaskStartedListener = onInboxTaskStartedListener; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onPageRequested(int index) { |
||||
|
pageIndicatorIndex.setValue(index); |
||||
|
loadInboxPage(index); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onNetworkTaskFinished(int resultCode, Inbox inbox) { |
||||
|
this.inbox = inbox; |
||||
|
onInboxTaskFinishedListener.onNetworkTaskFinished(resultCode, inbox); |
||||
|
if (resultCode == NetworkResultCodes.SUCCESSFUL) { |
||||
|
userExtraInfoVisibile.clear(); |
||||
|
for (PM pm : inbox.getPms()) |
||||
|
userExtraInfoVisibile.add(false); |
||||
|
|
||||
|
pageIndicatorIndex.setValue(inbox.getCurrentPageIndex()); |
||||
|
pageCount.setValue(inbox.getNumberOfPages()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void setOnInboxTaskCancelledListener(InboxTask.OnTaskCancelledListener onInboxTaskCancelledListener) { |
||||
|
this.onInboxTaskCancelledListener = onInboxTaskCancelledListener; |
||||
|
} |
||||
|
|
||||
|
public Inbox getInbox() { |
||||
|
return inbox; |
||||
|
} |
||||
|
|
||||
|
public MutableLiveData<Integer> getPageCount() { |
||||
|
return pageCount; |
||||
|
} |
||||
|
|
||||
|
public MutableLiveData<Integer> getPageIndicatorIndex() { |
||||
|
return pageIndicatorIndex; |
||||
|
} |
||||
|
|
||||
|
public boolean isUserExtraInfoVisible(int position) { |
||||
|
return userExtraInfoVisibile.get(position); |
||||
|
} |
||||
|
|
||||
|
public void hideUserInfo(int position) { |
||||
|
userExtraInfoVisibile.set(position, false); |
||||
|
} |
||||
|
|
||||
|
public void toggleUserInfo(int position) { |
||||
|
userExtraInfoVisibile.set(position, !userExtraInfoVisibile.get(position)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="24dp" android:tint="#FFFFFF" |
||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#FF000000" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/> |
||||
|
</vector> |
@ -0,0 +1,56 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:fitsSystemWindows="true" |
||||
|
tools:context=".activities.create_pm.CreatePMActivity"> |
||||
|
|
||||
|
<RelativeLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:id="@+id/appbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:paddingTop="@dimen/appbar_padding_top" |
||||
|
android:theme="@style/ToolbarTheme"> |
||||
|
|
||||
|
<androidx.appcompat.widget.Toolbar |
||||
|
android:id="@+id/toolbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="?attr/actionBarSize" |
||||
|
android:background="?attr/colorPrimary" |
||||
|
android:gravity="center" |
||||
|
app:popupTheme="@style/ToolbarTheme"></androidx.appcompat.widget.Toolbar> |
||||
|
</com.google.android.material.appbar.AppBarLayout> |
||||
|
|
||||
|
<include |
||||
|
layout="@layout/full_post_editor" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_below="@id/appbar" /> |
||||
|
|
||||
|
<gr.thmmy.mthmmy.editorview.EmojiKeyboard |
||||
|
android:id="@+id/emoji_keyboard" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentBottom="true" |
||||
|
android:visibility="gone" /> |
||||
|
</RelativeLayout> |
||||
|
|
||||
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar |
||||
|
android:id="@+id/progressBar" |
||||
|
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="@dimen/progress_bar_height" |
||||
|
android:indeterminate="true" |
||||
|
android:visibility="invisible" |
||||
|
app:layout_anchor="@id/appbar" |
||||
|
app:layout_anchorGravity="bottom|center" |
||||
|
app:mpb_indeterminateTint="@color/accent" |
||||
|
app:mpb_progressStyle="horizontal" /> |
||||
|
|
||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
@ -0,0 +1,71 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:orientation="vertical" |
||||
|
tools:context=".activities.inbox.InboxActivity"> |
||||
|
|
||||
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar |
||||
|
android:id="@+id/progress_bar" |
||||
|
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="@dimen/progress_bar_height" |
||||
|
android:indeterminate="true" |
||||
|
android:visibility="invisible" |
||||
|
app:layout_anchor="@id/appbar" |
||||
|
app:layout_anchorGravity="bottom|center" |
||||
|
app:mpb_indeterminateTint="@color/accent" |
||||
|
app:mpb_progressStyle="horizontal" /> |
||||
|
|
||||
|
<RelativeLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:id="@+id/appbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:theme="@style/ToolbarTheme"> |
||||
|
|
||||
|
<androidx.appcompat.widget.Toolbar |
||||
|
android:id="@+id/toolbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:background="?attr/colorPrimary" |
||||
|
android:gravity="center" |
||||
|
app:popupTheme="@style/ToolbarTheme" /> |
||||
|
</com.google.android.material.appbar.AppBarLayout> |
||||
|
|
||||
|
<androidx.recyclerview.widget.RecyclerView |
||||
|
android:id="@+id/inbox_recyclerview" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_below="@id/appbar" |
||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" |
||||
|
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" |
||||
|
tools:listitem="@layout/activity_inbox_pm_row" /> |
||||
|
</RelativeLayout> |
||||
|
|
||||
|
<gr.thmmy.mthmmy.pagination.BottomPaginationView |
||||
|
android:id="@+id/bottom_pagination" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="50dp" |
||||
|
android:layout_gravity="bottom|end" |
||||
|
android:background="@color/primary" |
||||
|
app:elevation="8dp" |
||||
|
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareLinearBehavior"/> |
||||
|
|
||||
|
<!-- |
||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton |
||||
|
android:id="@+id/send_pm_fab" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_gravity="bottom|end" |
||||
|
android:layout_marginBottom="50dp" |
||||
|
android:layout_marginEnd="@dimen/fab_margins" |
||||
|
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" |
||||
|
app:srcCompat="@drawable/ic_message_white_24dp" /> |
||||
|
--> |
||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
@ -0,0 +1,49 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/pm_quote_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="35dp" |
||||
|
android:background="?android:attr/selectableItemBackground" |
||||
|
android:drawablePadding="5dp" |
||||
|
android:gravity="center_vertical" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingEnd="12dp" |
||||
|
android:paddingStart="12dp" |
||||
|
android:paddingTop="6dp" |
||||
|
android:text="@string/quote_menu_button_text" |
||||
|
android:textColor="@color/primary_text" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/pm_reply_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="35dp" |
||||
|
android:background="?android:attr/selectableItemBackground" |
||||
|
android:drawablePadding="5dp" |
||||
|
android:gravity="center_vertical" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingEnd="12dp" |
||||
|
android:paddingStart="12dp" |
||||
|
android:paddingTop="6dp" |
||||
|
android:text="@string/reply_menu_button_text" |
||||
|
android:textColor="@color/primary_text" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/delete_post" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="35dp" |
||||
|
android:background="?android:attr/selectableItemBackground" |
||||
|
android:drawablePadding="5dp" |
||||
|
android:gravity="center_vertical" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingEnd="12dp" |
||||
|
android:paddingStart="12dp" |
||||
|
android:paddingTop="6dp" |
||||
|
android:text="@string/post_delete_button" |
||||
|
android:textColor="@color/primary_text" /> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,212 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical" |
||||
|
android:paddingEnd="4dp" |
||||
|
android:paddingStart="4dp" |
||||
|
tools:ignore="SmallSp"> |
||||
|
|
||||
|
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" |
||||
|
android:id="@+id/card_view" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_gravity="center" |
||||
|
android:foreground="?android:attr/selectableItemBackground" |
||||
|
card_view:cardBackgroundColor="@color/card_background" |
||||
|
card_view:cardCornerRadius="5dp" |
||||
|
card_view:cardElevation="2dp" |
||||
|
card_view:cardPreventCornerOverlap="false" |
||||
|
card_view:cardUseCompatPadding="true"> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:id="@+id/card_child_linear" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:orientation="horizontal"> |
||||
|
|
||||
|
<RelativeLayout |
||||
|
android:id="@+id/header" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_weight="1" |
||||
|
android:clickable="true" |
||||
|
android:focusable="true" |
||||
|
android:paddingLeft="16dp" |
||||
|
android:paddingRight="16dp" |
||||
|
android:paddingTop="16dp"> |
||||
|
|
||||
|
<FrameLayout |
||||
|
android:id="@+id/thumbnail_holder" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:layout_centerVertical="true" |
||||
|
android:layout_marginEnd="16dp"> |
||||
|
|
||||
|
<ImageView |
||||
|
android:id="@+id/thumbnail" |
||||
|
android:layout_width="@dimen/thumbnail_size" |
||||
|
android:layout_height="@dimen/thumbnail_size" |
||||
|
android:layout_gravity="center" |
||||
|
android:adjustViewBounds="true" |
||||
|
android:contentDescription="@string/personal_message_thumbnail" |
||||
|
android:transitionName="user_thumbnail" |
||||
|
app:srcCompat="@drawable/ic_default_user_avatar_darker" /> |
||||
|
</FrameLayout> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/username" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:layout_toEndOf="@+id/thumbnail_holder" |
||||
|
android:ellipsize="end" |
||||
|
android:maxLines="1" |
||||
|
tools:text="@string/pm_author" |
||||
|
android:textColor="@color/primary_text" |
||||
|
android:textStyle="bold" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/subject" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_below="@+id/username" |
||||
|
android:layout_toEndOf="@+id/thumbnail_holder" |
||||
|
android:ellipsize="end" |
||||
|
android:maxLines="1" |
||||
|
tools:text="@string/pm_subject" /> |
||||
|
</RelativeLayout> |
||||
|
<ImageButton |
||||
|
android:id="@+id/pm_overflow_menu" |
||||
|
android:layout_width="@dimen/post_image_button" |
||||
|
android:layout_height="@dimen/post_image_button" |
||||
|
android:layout_marginTop="9dp" |
||||
|
android:layout_marginEnd="9dp" |
||||
|
android:background="@color/card_background" |
||||
|
android:clickable="true" |
||||
|
android:contentDescription="@string/post_overflow_menu_button" |
||||
|
android:focusable="true" |
||||
|
app:srcCompat="@drawable/ic_more_vert_white_24dp" /> |
||||
|
</LinearLayout> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:id="@+id/user_extra_info" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:clickable="true" |
||||
|
android:focusable="true" |
||||
|
android:orientation="vertical" |
||||
|
android:paddingLeft="16dp" |
||||
|
android:paddingRight="16dp" |
||||
|
android:paddingTop="3dp" |
||||
|
android:visibility="gone" |
||||
|
android:weightSum="1.0"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/special_rank" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textColor="@color/card_expand_text_color" |
||||
|
android:textSize="10sp" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/rank" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textColor="@color/card_expand_text_color" |
||||
|
android:textSize="10sp" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/stars" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="2dp" |
||||
|
android:textSize="10sp" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/gender" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textColor="@color/card_expand_text_color" |
||||
|
android:textSize="10sp" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/number_of_posts" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textColor="@color/card_expand_text_color" |
||||
|
android:textSize="10sp" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/personal_text" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:textColor="@color/card_expand_text_color" |
||||
|
android:textSize="10sp" |
||||
|
android:textStyle="italic" |
||||
|
android:visibility="gone" /> |
||||
|
|
||||
|
</LinearLayout> |
||||
|
|
||||
|
<FrameLayout |
||||
|
android:id="@+id/pm_date_exp" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginLeft="16dp" |
||||
|
android:layout_marginRight="16dp" |
||||
|
android:layout_marginTop="16dp"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/pm_date" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:gravity="start" |
||||
|
android:text="" |
||||
|
android:textColor="@color/accent" |
||||
|
android:textSize="11sp" /> |
||||
|
</FrameLayout> |
||||
|
|
||||
|
|
||||
|
<View |
||||
|
android:id="@+id/header_body_divider" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="1dp" |
||||
|
android:layout_marginBottom="9dp" |
||||
|
android:layout_marginLeft="16dp" |
||||
|
android:layout_marginRight="16dp" |
||||
|
android:layout_marginTop="5dp" |
||||
|
android:background="@color/divider" /> |
||||
|
|
||||
|
<FrameLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_marginBottom="16dp"> |
||||
|
|
||||
|
<WebView |
||||
|
android:id="@+id/pm" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_gravity="center" |
||||
|
android:layout_marginLeft="16dp" |
||||
|
android:layout_marginRight="16dp" |
||||
|
android:background="@color/card_background" |
||||
|
android:clickable="true" |
||||
|
android:focusable="true" |
||||
|
tools:text="@string/pm_content" /> |
||||
|
</FrameLayout> |
||||
|
</LinearLayout> |
||||
|
</androidx.cardview.widget.CardView> |
||||
|
</LinearLayout> |
@ -0,0 +1,32 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:orientation="vertical" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||
|
|
||||
|
<com.google.android.material.textfield.TextInputLayout |
||||
|
android:id="@+id/subject_input" |
||||
|
android:layout_width="240dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_margin="16dp" |
||||
|
android:hint="@string/subject"> |
||||
|
|
||||
|
<com.google.android.material.textfield.TextInputEditText |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:lines="1" |
||||
|
android:maxLines="1" |
||||
|
android:ellipsize="end" |
||||
|
android:inputType="text"/> |
||||
|
</com.google.android.material.textfield.TextInputLayout> |
||||
|
|
||||
|
<gr.thmmy.mthmmy.editorview.EditorView |
||||
|
android:id="@+id/main_content_editorview" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginStart="16dp" |
||||
|
android:layout_marginEnd="16dp" |
||||
|
app:hint="topic message"/> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:background="@color/primary" |
||||
|
app:elevation="8dp" |
||||
|
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareLinearBehavior"> |
||||
|
<ImageButton |
||||
|
android:id="@+id/page_first_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_weight="0.8" |
||||
|
android:background="?attr/selectableItemBackgroundBorderless" |
||||
|
android:contentDescription="@string/button_first" |
||||
|
app:srcCompat="@drawable/page_first" /> |
||||
|
|
||||
|
<ImageButton |
||||
|
android:id="@+id/page_previous_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_weight="0.8" |
||||
|
android:background="?attr/selectableItemBackgroundBorderless" |
||||
|
android:contentDescription="@string/button_previous" |
||||
|
app:srcCompat="@drawable/page_previous" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/page_indicator" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_gravity="center" |
||||
|
android:layout_weight="1" |
||||
|
android:gravity="center" |
||||
|
android:hint="@string/button_page" |
||||
|
android:maxLines="1" |
||||
|
android:textColor="@color/white" |
||||
|
android:textSize="22sp" /> |
||||
|
|
||||
|
<ImageButton |
||||
|
android:id="@+id/page_next_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_weight="0.8" |
||||
|
android:background="?attr/selectableItemBackgroundBorderless" |
||||
|
android:contentDescription="@string/button_next" |
||||
|
app:srcCompat="@drawable/page_next" /> |
||||
|
|
||||
|
<ImageButton |
||||
|
android:id="@+id/page_last_button" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_weight="0.8" |
||||
|
android:background="?attr/selectableItemBackgroundBorderless" |
||||
|
android:contentDescription="@string/button_last" |
||||
|
app:srcCompat="@drawable/page_last" /> |
||||
|
</merge> |
@ -0,0 +1,19 @@ |
|||||
|
package gr.thmmy.mthmmy.utils.parsing; |
||||
|
|
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
|
||||
|
import static org.junit.Assert.*; |
||||
|
|
||||
|
public class StringUtilsTest { |
||||
|
|
||||
|
@Before |
||||
|
public void setUp() throws Exception { |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void extractUserCodeFromUrl() { |
||||
|
String url = "https://www.thmmy.gr/smf/index.php?action=profile;u=14670"; |
||||
|
assertEquals(StringUtils.extractUserCodeFromUrl(url), 14670); |
||||
|
} |
||||
|
} |
@ -1,6 +1,6 @@ |
|||||
#Tue Sep 17 12:32:34 EEST 2019 |
#Mon Mar 30 15:17:37 EEST 2020 |
||||
distributionBase=GRADLE_USER_HOME |
distributionBase=GRADLE_USER_HOME |
||||
distributionPath=wrapper/dists |
distributionPath=wrapper/dists |
||||
zipStoreBase=GRADLE_USER_HOME |
zipStoreBase=GRADLE_USER_HOME |
||||
zipStorePath=wrapper/dists |
zipStorePath=wrapper/dists |
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip |
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip |
||||
|
Loading…
Reference in new issue