mirror of https://github.com/ThmmyNoLife/mTHMMY
Ezerous
6 years ago
133 changed files with 3290 additions and 1331 deletions
@ -0,0 +1,51 @@ |
|||||
|
import org.ajoberstar.grgit.Grgit |
||||
|
|
||||
|
buildscript { |
||||
|
repositories { |
||||
|
jcenter() |
||||
|
} |
||||
|
|
||||
|
dependencies { |
||||
|
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static def getCurrentBranch() { |
||||
|
try { |
||||
|
def grgit = Grgit.open() |
||||
|
def currentBranch = grgit.branch.getCurrent().name |
||||
|
grgit.close() |
||||
|
return currentBranch |
||||
|
} catch (Exception ignored) { |
||||
|
return "" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static def getCommitHash() { |
||||
|
try { |
||||
|
def grgit = Grgit.open() |
||||
|
def commitHash = grgit.head().id |
||||
|
grgit.close() |
||||
|
return commitHash |
||||
|
} catch (Exception ignored) { |
||||
|
return "" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Will return true if there are no uncommitted changes |
||||
|
static def isClean() { |
||||
|
try { |
||||
|
def grgit = Grgit.open() |
||||
|
def isClean = grgit.status().isClean() |
||||
|
grgit.close() |
||||
|
return isClean |
||||
|
} catch (Exception ignored) { |
||||
|
return true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ext { |
||||
|
getCurrentBranch = this.&getCurrentBranch |
||||
|
getCommitHash = this.&getCommitHash |
||||
|
isClean = this.&isClean |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.shoutbox; |
||||
|
|
||||
|
import org.jsoup.nodes.Document; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.utils.NetworkResultCodes; |
||||
|
import gr.thmmy.mthmmy.utils.NetworkTask; |
||||
|
import okhttp3.MultipartBody; |
||||
|
import okhttp3.OkHttpClient; |
||||
|
import okhttp3.Request; |
||||
|
import okhttp3.Response; |
||||
|
|
||||
|
public class SendShoutTask extends NetworkTask<Void> { |
||||
|
|
||||
|
public SendShoutTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener<Void> onNetworkTaskFinishedListener) { |
||||
|
super(onTaskStartedListener, onNetworkTaskFinishedListener); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Response sendRequest(OkHttpClient client, String... input) throws IOException { |
||||
|
MultipartBody.Builder postBodyBuilder = new MultipartBody.Builder() |
||||
|
.setType(MultipartBody.FORM) |
||||
|
.addFormDataPart("sc", input[2]) |
||||
|
.addFormDataPart("tp-shout", input[1]) |
||||
|
.addFormDataPart("tp-shout-name", input[3]) |
||||
|
.addFormDataPart("shout_send", input[4]) |
||||
|
.addFormDataPart("tp-shout-url", input[5]); |
||||
|
|
||||
|
Request voteRequest = new Request.Builder() |
||||
|
.url(input[0]) |
||||
|
.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(postBodyBuilder.build()) |
||||
|
.build(); |
||||
|
client.newCall(voteRequest).execute(); |
||||
|
return client.newCall(voteRequest).execute(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
protected Void performTask(Document document, Response response) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected int getResultCode(Response response, Void data) { |
||||
|
return NetworkResultCodes.SUCCESSFUL; |
||||
|
} |
||||
|
} |
@ -0,0 +1,155 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.shoutbox; |
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.Context; |
||||
|
import android.content.Intent; |
||||
|
import android.graphics.Color; |
||||
|
import android.net.Uri; |
||||
|
import android.os.Build; |
||||
|
import android.os.Bundle; |
||||
|
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.TextView; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.activities.board.BoardActivity; |
||||
|
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; |
||||
|
import gr.thmmy.mthmmy.activities.topic.TopicActivity; |
||||
|
import gr.thmmy.mthmmy.model.Shout; |
||||
|
import gr.thmmy.mthmmy.model.ThmmyPage; |
||||
|
import gr.thmmy.mthmmy.utils.CustomRecyclerView; |
||||
|
|
||||
|
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; |
||||
|
|
||||
|
public class ShoutAdapter extends CustomRecyclerView.Adapter<ShoutAdapter.ShoutViewHolder> { |
||||
|
private Context context; |
||||
|
private Shout[] shouts; |
||||
|
|
||||
|
ShoutAdapter(Context context, Shout[] shouts) { |
||||
|
this.context = context; |
||||
|
this.shouts = shouts; |
||||
|
} |
||||
|
|
||||
|
void setShouts(Shout[] shouts) { |
||||
|
this.shouts = shouts; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public ShoutViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
||||
|
View view = LayoutInflater.from(parent.getContext()) |
||||
|
.inflate(R.layout.fragment_shoutbox_shout_row, parent, false); |
||||
|
return new ShoutViewHolder(view); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull ShoutViewHolder holder, int position) { |
||||
|
Shout currentShout = shouts[position]; |
||||
|
holder.author.setText(currentShout.getShouter()); |
||||
|
if (currentShout.isMemberOfTheMonth()) holder.author.setTextColor(context.getResources().getColor(R.color.member_of_the_month)); |
||||
|
else holder.author.setTextColor(context.getResources().getColor(R.color.accent)); |
||||
|
holder.author.setOnClickListener(view -> { |
||||
|
Intent intent = new Intent(context, ProfileActivity.class); |
||||
|
Bundle extras = new Bundle(); |
||||
|
extras.putString(BUNDLE_PROFILE_URL, shouts[holder.getAdapterPosition()].getShouterProfileURL()); |
||||
|
extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, ""); |
||||
|
extras.putString(BUNDLE_PROFILE_USERNAME, ""); |
||||
|
intent.putExtras(extras); |
||||
|
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); |
||||
|
context.startActivity(intent); |
||||
|
}); |
||||
|
holder.dateTime.setText(currentShout.getDate()); |
||||
|
holder.shoutContent.setClickable(true); |
||||
|
holder.shoutContent.setWebViewClient(new LinkLauncher()); |
||||
|
holder.shoutContent.loadDataWithBaseURL("file:///android_asset/", currentShout.getShout(), |
||||
|
"text/html", "UTF-8", null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemCount() { |
||||
|
return shouts.length; |
||||
|
} |
||||
|
|
||||
|
static class ShoutViewHolder extends CustomRecyclerView.ViewHolder { |
||||
|
|
||||
|
TextView author, dateTime; |
||||
|
WebView shoutContent; |
||||
|
|
||||
|
ShoutViewHolder(@NonNull View itemView) { |
||||
|
super(itemView); |
||||
|
author = itemView.findViewById(R.id.author_textview); |
||||
|
dateTime = itemView.findViewById(R.id.date_time_textview); |
||||
|
shoutContent = itemView.findViewById(R.id.shout_content); |
||||
|
shoutContent.setBackgroundColor(Color.argb(1, 255, 255, 255)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class LinkLauncher extends WebViewClient { |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||
|
@Override |
||||
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { |
||||
|
final Uri uri = request.getUrl(); |
||||
|
return handleUri(uri); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) { |
||||
|
final Uri uri = Uri.parse(url); |
||||
|
return handleUri(uri); |
||||
|
} |
||||
|
|
||||
|
private boolean handleUri(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,54 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.shoutbox; |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.base.BaseActivity; |
||||
|
|
||||
|
public class ShoutboxActivity extends BaseActivity { |
||||
|
|
||||
|
private ShoutboxFragment shoutboxFragment; |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.activity_shoutbox); |
||||
|
|
||||
|
//Initialize toolbar
|
||||
|
toolbar = findViewById(R.id.toolbar); |
||||
|
toolbar.setTitle("Shoutbox"); |
||||
|
setSupportActionBar(toolbar); |
||||
|
if (getSupportActionBar() != null) { |
||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true); |
||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true); |
||||
|
} |
||||
|
|
||||
|
createDrawer(); |
||||
|
drawer.setSelection(SHOUTBOX_ID); |
||||
|
|
||||
|
if (savedInstanceState == null) { |
||||
|
shoutboxFragment = ShoutboxFragment.newInstance(); |
||||
|
getSupportFragmentManager().beginTransaction() |
||||
|
.replace(R.id.container, shoutboxFragment) |
||||
|
.commitNow(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onResume() { |
||||
|
drawer.setSelection(SHOUTBOX_ID); |
||||
|
super.onResume(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBackPressed() { |
||||
|
int count = getSupportFragmentManager().getBackStackEntryCount(); |
||||
|
|
||||
|
if (count == 0) { |
||||
|
if (!shoutboxFragment.onBackPressed()) |
||||
|
super.onBackPressed(); |
||||
|
} else { |
||||
|
getSupportFragmentManager().popBackStack(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,173 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.shoutbox; |
||||
|
|
||||
|
import android.app.Activity; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Bundle; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.view.inputmethod.InputMethodManager; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
import androidx.lifecycle.ViewModelProviders; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import gr.thmmy.mthmmy.R; |
||||
|
import gr.thmmy.mthmmy.editorview.EditorView; |
||||
|
import gr.thmmy.mthmmy.editorview.EmojiKeyboard; |
||||
|
import gr.thmmy.mthmmy.model.Shout; |
||||
|
import gr.thmmy.mthmmy.model.Shoutbox; |
||||
|
import gr.thmmy.mthmmy.session.SessionManager; |
||||
|
import gr.thmmy.mthmmy.utils.CustomRecyclerView; |
||||
|
import gr.thmmy.mthmmy.utils.NetworkResultCodes; |
||||
|
import gr.thmmy.mthmmy.viewmodel.ShoutboxViewModel; |
||||
|
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; |
||||
|
import timber.log.Timber; |
||||
|
|
||||
|
public class ShoutboxFragment extends Fragment { |
||||
|
|
||||
|
private MaterialProgressBar progressBar; |
||||
|
private ShoutboxTask shoutboxTask; |
||||
|
private ShoutAdapter shoutAdapter; |
||||
|
private EmojiKeyboard emojiKeyboard; |
||||
|
private EditorView editorView; |
||||
|
|
||||
|
private ShoutboxViewModel shoutboxViewModel; |
||||
|
|
||||
|
public static ShoutboxFragment newInstance() { |
||||
|
return new ShoutboxFragment(); |
||||
|
} |
||||
|
|
||||
|
@Nullable |
||||
|
@Override |
||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, |
||||
|
@Nullable Bundle savedInstanceState) { |
||||
|
final View rootView = inflater.inflate(R.layout.fragment_shoutbox, container, false); |
||||
|
setHasOptionsMenu(true); |
||||
|
|
||||
|
progressBar = rootView.findViewById(R.id.progressBar); |
||||
|
CustomRecyclerView recyclerView = rootView.findViewById(R.id.shoutbox_recyclerview); |
||||
|
shoutAdapter = new ShoutAdapter(getContext(), new Shout[0]); |
||||
|
recyclerView.setAdapter(shoutAdapter); |
||||
|
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); |
||||
|
layoutManager.setReverseLayout(true); |
||||
|
recyclerView.setLayoutManager(layoutManager); |
||||
|
recyclerView.setOnTouchListener((view, motionEvent) -> { |
||||
|
editorView.hideMarkdown(); |
||||
|
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); |
||||
|
imm.hideSoftInputFromWindow(editorView.getWindowToken(), 0); |
||||
|
return false; |
||||
|
}); |
||||
|
|
||||
|
emojiKeyboard = rootView.findViewById(R.id.emoji_keyboard); |
||||
|
editorView = rootView.findViewById(R.id.edior_view); |
||||
|
editorView.setEmojiKeyboard(emojiKeyboard); |
||||
|
emojiKeyboard.registerEmojiInputField(editorView); |
||||
|
editorView.setOnSubmitListener(view -> { |
||||
|
if (shoutboxViewModel.getShoutboxMutableLiveData().getValue() == null) return; |
||||
|
if (editorView.getText().toString().isEmpty()) { |
||||
|
editorView.setError("Required"); |
||||
|
return; |
||||
|
} |
||||
|
shoutboxViewModel.sendShout(editorView.getText().toString()); |
||||
|
}); |
||||
|
editorView.hideMarkdown(); |
||||
|
editorView.showMarkdownOnfocus(); |
||||
|
|
||||
|
return rootView; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
||||
|
inflater.inflate(R.menu.shoutbox_menu, menu); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
if (item.getItemId() == R.id.menu_refresh) { |
||||
|
shoutboxViewModel.loadShoutbox(); |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) { |
||||
|
super.onActivityCreated(savedInstanceState); |
||||
|
shoutboxViewModel = ViewModelProviders.of(this).get(ShoutboxViewModel.class); |
||||
|
shoutboxViewModel.getShoutboxMutableLiveData().observe(this, shoutbox -> { |
||||
|
if (shoutbox != null) { |
||||
|
Timber.i("Shoutbox loaded successfully"); |
||||
|
shoutAdapter.setShouts(shoutbox.getShouts()); |
||||
|
shoutAdapter.notifyDataSetChanged(); |
||||
|
} |
||||
|
}); |
||||
|
shoutboxViewModel.setOnShoutboxTaskStarted(this::onShoutboxTaskSarted); |
||||
|
shoutboxViewModel.setOnShoutboxTaskFinished(this::onShoutboxTaskFinished); |
||||
|
shoutboxViewModel.setOnSendShoutTaskStarted(this::onSendShoutTaskStarted); |
||||
|
shoutboxViewModel.setOnSendShoutTaskFinished(this::onSendShoutTaskFinished); |
||||
|
|
||||
|
shoutboxViewModel.loadShoutbox(); |
||||
|
} |
||||
|
|
||||
|
private void onShoutboxTaskSarted() { |
||||
|
Timber.i("Starting shoutbox task..."); |
||||
|
progressBar.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
private void onSendShoutTaskStarted() { |
||||
|
Timber.i("Start sending a shout..."); |
||||
|
editorView.setAlpha(0.5f); |
||||
|
editorView.setEnabled(false); |
||||
|
if (emojiKeyboard.isVisible()) |
||||
|
emojiKeyboard.setVisibility(View.GONE); |
||||
|
progressBar.setVisibility(View.VISIBLE); |
||||
|
} |
||||
|
|
||||
|
private void onSendShoutTaskFinished(int resultCode, Void ignored) { |
||||
|
editorView.setAlpha(1f); |
||||
|
editorView.setEnabled(true); |
||||
|
progressBar.setVisibility(View.INVISIBLE); |
||||
|
if (resultCode == NetworkResultCodes.SUCCESSFUL) { |
||||
|
Timber.i("Shout was sent successfully"); |
||||
|
editorView.getEditText().getText().clear(); |
||||
|
shoutboxTask = new ShoutboxTask(ShoutboxFragment.this::onShoutboxTaskSarted, ShoutboxFragment.this::onShoutboxTaskFinished); |
||||
|
shoutboxTask.execute(SessionManager.shoutboxUrl.toString()); |
||||
|
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { |
||||
|
Timber.w("Failed to send shout"); |
||||
|
Toast.makeText(getContext(), "NetworkError", Toast.LENGTH_SHORT).show(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void onShoutboxTaskFinished(int resultCode, Shoutbox shoutbox) { |
||||
|
progressBar.setVisibility(View.INVISIBLE); |
||||
|
if (resultCode == NetworkResultCodes.SUCCESSFUL) { |
||||
|
shoutboxViewModel.setShoutbox(shoutbox); |
||||
|
if (shoutbox.getShoutSend() != null) |
||||
|
editorView.setVisibility(View.VISIBLE); |
||||
|
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { |
||||
|
Timber.w("Failed to retreive shoutbox due to network error"); |
||||
|
Toast.makeText(getContext(), "NetworkError", Toast.LENGTH_SHORT).show(); |
||||
|
} else { |
||||
|
Timber.wtf("Failed to retreive shoutbox due to unknown error"); |
||||
|
Toast.makeText(getContext(), "Failed to retrieve shoutbox, please contact mthmmy developer team", Toast.LENGTH_LONG).show(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return whether or not {@link ShoutboxFragment#onBackPressed()} consumed the event or not |
||||
|
*/ |
||||
|
public boolean onBackPressed() { |
||||
|
if (emojiKeyboard.isVisible()) { |
||||
|
emojiKeyboard.setVisibility(View.GONE); |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.shoutbox; |
||||
|
|
||||
|
import org.jsoup.nodes.Document; |
||||
|
import org.jsoup.nodes.Element; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.model.Shout; |
||||
|
import gr.thmmy.mthmmy.model.Shoutbox; |
||||
|
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 ShoutboxTask extends NewParseTask<Shoutbox> { |
||||
|
|
||||
|
public ShoutboxTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener<Shoutbox> onParseTaskFinishedListener) { |
||||
|
super(onTaskStartedListener, onParseTaskFinishedListener); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected Shoutbox parse(Document document, Response response) throws ParseException { |
||||
|
// fragment_shoutbox_shout_row container: document.select("div[class=smalltext]" && div.text().contains("Τελευταίες 75 φωνές:") η στα αγγλικα
|
||||
|
Element shoutboxContainer = document.select("table.windowbg").first(); |
||||
|
ArrayList<Shout> shouts = new ArrayList<>(); |
||||
|
for (Element shout : shoutboxContainer.select("div[style=margin: 4px;]")) { |
||||
|
Element user = shout.child(0); |
||||
|
Element link = user.select("a").first(); |
||||
|
String profileUrl = link.attr("href"); |
||||
|
String profileName = link.text(); |
||||
|
boolean memberOfTheMonth = link.attr("style").contains("#EA00FF"); |
||||
|
|
||||
|
Element date = shout.child(1); |
||||
|
String dateString = date.text(); |
||||
|
|
||||
|
Element content = shout.child(2); |
||||
|
content.removeAttr("style"); |
||||
|
String shoutContent = "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + |
||||
|
ParseHelpers.youtubeEmbeddedFix(content); |
||||
|
shouts.add(new Shout(profileName, profileUrl, dateString, shoutContent, memberOfTheMonth)); |
||||
|
} |
||||
|
|
||||
|
Element shoutboxForm = document.select("form[name=tp-shoutbox]").first(); |
||||
|
String formUrl = shoutboxForm.attr("action"); |
||||
|
String sc = shoutboxForm.select("input[name=sc]").first().attr("value"); |
||||
|
String shoutName = shoutboxForm.select("input[name=tp-shout-name]").first().attr("value"); |
||||
|
// TODO: make shout send nullable and disable shouting
|
||||
|
Element shoutSendInput = shoutboxForm.select("input[name=shout_send]").first(); |
||||
|
String shoutSend = null; |
||||
|
if (shoutSendInput != null) |
||||
|
shoutSend = shoutSendInput.attr("value"); |
||||
|
String shoutUrl = shoutboxForm.select("input[name=tp-shout-url]").first().attr("value"); |
||||
|
return new Shoutbox(shouts.toArray(new Shout[0]), sc, formUrl, shoutName, shoutSend, shoutUrl); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected int getResultCode(Response response, Shoutbox data) { |
||||
|
return NetworkResultCodes.SUCCESSFUL; |
||||
|
} |
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
public class BBTag { |
||||
|
private int start, end; |
||||
|
private String name, attribute; |
||||
|
|
||||
|
public BBTag(int start, String name) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public BBTag(int start, String name, String attribute) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
this.attribute = attribute; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "start:" + start + ",end:" + end + ",name:" + name; |
||||
|
} |
||||
|
|
||||
|
public int getStart() { |
||||
|
return start; |
||||
|
} |
||||
|
|
||||
|
public void setStart(int start) { |
||||
|
this.start = start; |
||||
|
} |
||||
|
|
||||
|
public int getEnd() { |
||||
|
return end; |
||||
|
} |
||||
|
|
||||
|
public void setEnd(int end) { |
||||
|
this.end = end; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public String getAttribute() { |
||||
|
return attribute; |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
public class HtmlTag { |
||||
|
private int start, end; |
||||
|
private String name, attributeKey, attributeValue; |
||||
|
|
||||
|
public HtmlTag(int start, String name) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public HtmlTag(int start, String name, String attributeKey, String attributeValue) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
this.attributeKey = attributeKey; |
||||
|
this.attributeValue = attributeValue; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "start:" + start + ",end:" + end + ",name:" + name; |
||||
|
} |
||||
|
|
||||
|
public int getStart() { |
||||
|
return start; |
||||
|
} |
||||
|
|
||||
|
public void setStart(int start) { |
||||
|
this.start = start; |
||||
|
} |
||||
|
|
||||
|
public int getEnd() { |
||||
|
return end; |
||||
|
} |
||||
|
|
||||
|
public void setEnd(int end) { |
||||
|
this.end = end; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public String getAttributeKey() { |
||||
|
return attributeKey; |
||||
|
} |
||||
|
|
||||
|
public String getAttributeValue() { |
||||
|
return attributeValue; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
public class Shout { |
||||
|
private final String shouter, shouterProfileURL, date, shout; |
||||
|
private final boolean memberOfTheMonth; |
||||
|
|
||||
|
public Shout(String shouter, String shouterProfileURL, String date, String shout, boolean memberOfTheMonth) { |
||||
|
this.shouter = shouter; |
||||
|
this.shouterProfileURL = shouterProfileURL; |
||||
|
this.date = date; |
||||
|
this.shout = shout; |
||||
|
this.memberOfTheMonth = memberOfTheMonth; |
||||
|
} |
||||
|
|
||||
|
public String getShouter() { |
||||
|
return shouter; |
||||
|
} |
||||
|
|
||||
|
public String getShouterProfileURL() { |
||||
|
return shouterProfileURL; |
||||
|
} |
||||
|
|
||||
|
public String getDate() { |
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
public String getShout() { |
||||
|
return shout; |
||||
|
} |
||||
|
|
||||
|
public boolean isMemberOfTheMonth() { |
||||
|
return memberOfTheMonth; |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
public class Shoutbox { |
||||
|
private Shout[] shouts; |
||||
|
private String sc, sendShoutUrl, shoutName, shoutSend, shoutUrl; |
||||
|
|
||||
|
public Shoutbox(Shout[] shouts, String sc, String sendShoutUrl, String shoutName, String shoutSend, String shoutUrl) { |
||||
|
this.shouts = shouts; |
||||
|
this.sc = sc; |
||||
|
this.sendShoutUrl = sendShoutUrl; |
||||
|
this.shoutName = shoutName; |
||||
|
this.shoutSend = shoutSend; |
||||
|
this.shoutUrl = shoutUrl; |
||||
|
} |
||||
|
|
||||
|
public Shout[] getShouts() { |
||||
|
return shouts; |
||||
|
} |
||||
|
|
||||
|
public String getSc() { |
||||
|
return sc; |
||||
|
} |
||||
|
|
||||
|
public String getSendShoutUrl() { |
||||
|
return sendShoutUrl; |
||||
|
} |
||||
|
|
||||
|
public String getShoutName() { |
||||
|
return shoutName; |
||||
|
} |
||||
|
|
||||
|
public String getShoutSend() { |
||||
|
return shoutSend; |
||||
|
} |
||||
|
|
||||
|
public String getShoutUrl() { |
||||
|
return shoutUrl; |
||||
|
} |
||||
|
} |
@ -0,0 +1,221 @@ |
|||||
|
package gr.thmmy.mthmmy.utils.parsing; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.graphics.Typeface; |
||||
|
import android.text.Spannable; |
||||
|
import android.text.SpannableStringBuilder; |
||||
|
import android.text.TextUtils; |
||||
|
import android.text.style.StrikethroughSpan; |
||||
|
import android.text.style.StyleSpan; |
||||
|
import android.text.style.URLSpan; |
||||
|
import android.text.style.UnderlineSpan; |
||||
|
|
||||
|
import java.nio.charset.UnsupportedCharsetException; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.model.BBTag; |
||||
|
import gr.thmmy.mthmmy.model.HtmlTag; |
||||
|
import gr.thmmy.mthmmy.utils.HTMLUtils; |
||||
|
|
||||
|
public class ThmmyParser { |
||||
|
private static final String[] ALL_BB_TAGS = {"b", "i", "u", "s", "glow", "shadow", "move", "pre", "lefter", |
||||
|
"center", "right", "hr", "size", "font", "color", "youtube", "flash", "img", "url" |
||||
|
, "email", "ftp", "table", "tr", "td", "sup", "sub", "tt", "code", "quote", "tex", "list", "li"}; |
||||
|
private static final String[] ALL_HTML_TAGS = {"b", "br", "span", "i", "div", "del", "marquee", "pre", |
||||
|
"hr", "embed", "noembed", "a", "img", "table", "tr", "td", "sup", "sub", "tt", "pre", "ul", "li"}; |
||||
|
|
||||
|
public static SpannableStringBuilder bb2span(String bb) { |
||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(bb); |
||||
|
// store the original indices of the string
|
||||
|
LinkedList<Integer> stringIndices = new LinkedList<>(); |
||||
|
for (int i = 0; i < builder.length(); i++) { |
||||
|
stringIndices.add(i); |
||||
|
} |
||||
|
|
||||
|
BBTag[] tags = getBBTags(bb); |
||||
|
for (BBTag tag : tags) { |
||||
|
int start = stringIndices.indexOf(tag.getStart()); |
||||
|
int end = stringIndices.indexOf(tag.getEnd()); |
||||
|
int startTagLength = tag.getName().length() + 2; |
||||
|
int endTagLength = tag.getName().length() + 3; |
||||
|
switch (tag.getName()) { |
||||
|
case "b": |
||||
|
builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "i": |
||||
|
builder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "u": |
||||
|
builder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "s": |
||||
|
builder.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
default: |
||||
|
throw new UnsupportedCharsetException("Tag not supported"); |
||||
|
} |
||||
|
//remove starting and ending tag and and do the same changes in the list
|
||||
|
builder.delete(start, start + startTagLength); |
||||
|
for (int i = start; i < start + startTagLength; i++) { |
||||
|
stringIndices.remove(start); |
||||
|
} |
||||
|
builder.delete(end - startTagLength, end - startTagLength + endTagLength); |
||||
|
for (int i = end - startTagLength; i < end - startTagLength + endTagLength; i++) { |
||||
|
stringIndices.remove(end - startTagLength); |
||||
|
} |
||||
|
} |
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
public static SpannableStringBuilder html2span(Context context, String html) { |
||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(html); |
||||
|
// store the original indices of the string
|
||||
|
LinkedList<Integer> stringIndices = new LinkedList<>(); |
||||
|
for (int i = 0; i < builder.length(); i++) { |
||||
|
stringIndices.add(i); |
||||
|
} |
||||
|
|
||||
|
HtmlTag[] tags = getHtmlTags(html); |
||||
|
for (HtmlTag tag : tags) { |
||||
|
int start = stringIndices.indexOf(tag.getStart()); |
||||
|
int end = stringIndices.indexOf(tag.getEnd()); |
||||
|
int startTagLength = tag.getName().length() + 2; |
||||
|
if (tag.getAttributeKey() != null) { |
||||
|
startTagLength += tag.getAttributeKey().length() + tag.getAttributeValue().length() + 4; |
||||
|
} |
||||
|
int endTagLength = tag.getName().length() + 3; |
||||
|
|
||||
|
if (isHtmlTagSupported(tag.getName(), tag.getAttributeKey(), tag.getAttributeValue())) { |
||||
|
switch (tag.getName()) { |
||||
|
case "b": |
||||
|
builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "i": |
||||
|
builder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "span": |
||||
|
if (tag.getAttributeKey().equals("style") && tag.getAttributeValue().equals("text-decoration: underline;")) { |
||||
|
builder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
} |
||||
|
break; |
||||
|
case "del": |
||||
|
builder.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "a": |
||||
|
URLSpan urlSpan = new URLSpan(tag.getAttributeValue()); |
||||
|
builder.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
HTMLUtils.makeLinkClickable(context, builder, urlSpan); |
||||
|
break; |
||||
|
default: |
||||
|
throw new UnsupportedCharsetException("Tag not supported"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//remove starting and ending tag and and do the same changes in the list
|
||||
|
builder.delete(start, start + startTagLength); |
||||
|
for (int i = start; i < start + startTagLength; i++) { |
||||
|
stringIndices.remove(start); |
||||
|
} |
||||
|
builder.delete(end - startTagLength, end - startTagLength + endTagLength); |
||||
|
for (int i = end - startTagLength; i < end - startTagLength + endTagLength; i++) { |
||||
|
stringIndices.remove(end - startTagLength); |
||||
|
} |
||||
|
} |
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
public static BBTag[] getBBTags(String bb) { |
||||
|
Pattern bbtagPattern = Pattern.compile("\\[(.+?)\\]"); |
||||
|
|
||||
|
LinkedList<BBTag> tags = new LinkedList<>(); |
||||
|
Matcher bbMatcher = bbtagPattern.matcher(bb); |
||||
|
while (bbMatcher.find()) { |
||||
|
String startTag = bbMatcher.group(1); |
||||
|
int separatorIndex = startTag.indexOf('='); |
||||
|
String name, attribute = null; |
||||
|
if (separatorIndex > 0) { |
||||
|
attribute = startTag.substring(separatorIndex); |
||||
|
name = startTag.substring(0, separatorIndex); |
||||
|
} else |
||||
|
name = startTag; |
||||
|
|
||||
|
if (name.startsWith("/")) { |
||||
|
//closing tag
|
||||
|
name = name.substring(1); |
||||
|
for (int i = tags.size() - 1; i >= 0; i--) { |
||||
|
if (tags.get(i).getName().equals(name)) { |
||||
|
tags.get(i).setEnd(bbMatcher.start()); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
if (isBBTagSupported(name)) |
||||
|
tags.add(new BBTag(bbMatcher.start(), name, attribute)); |
||||
|
} |
||||
|
// remove parsed tags with no end tag
|
||||
|
for (BBTag bbTag : tags) |
||||
|
if (bbTag.getEnd() == 0) |
||||
|
tags.remove(bbTag); |
||||
|
return tags.toArray(new BBTag[0]); |
||||
|
} |
||||
|
|
||||
|
private static HtmlTag[] getHtmlTags(String html) { |
||||
|
Pattern htmlPattern = Pattern.compile("<(.+?)>"); |
||||
|
|
||||
|
LinkedList<HtmlTag> tags = new LinkedList<>(); |
||||
|
Matcher htmlMatcher = htmlPattern.matcher(html); |
||||
|
while (htmlMatcher.find()) { |
||||
|
String startTag = htmlMatcher.group(1); |
||||
|
int separatorIndex = startTag.indexOf(' '); |
||||
|
String name, attribute = null, attributeValue = null; |
||||
|
if (separatorIndex > 0) { |
||||
|
String fullAttribute = startTag.substring(separatorIndex); |
||||
|
int equalsIndex = fullAttribute.indexOf('='); |
||||
|
attribute = fullAttribute.substring(1, equalsIndex); |
||||
|
attributeValue = fullAttribute.substring(equalsIndex + 2, fullAttribute.length() - 1); |
||||
|
name = startTag.substring(0, separatorIndex); |
||||
|
} else |
||||
|
name = startTag; |
||||
|
|
||||
|
if (name.startsWith("/")) { |
||||
|
//closing tag
|
||||
|
name = name.substring(1); |
||||
|
for (int i = tags.size() - 1; i >= 0; i--) { |
||||
|
if (tags.get(i).getName().equals(name)) { |
||||
|
tags.get(i).setEnd(htmlMatcher.start()); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
if (isHtmlTag(name)) |
||||
|
tags.add(new HtmlTag(htmlMatcher.start(), name, attribute, attributeValue)); |
||||
|
} |
||||
|
// remove parsed tags with no end tag
|
||||
|
for (HtmlTag htmlTag : tags) |
||||
|
if (htmlTag.getEnd() == 0) |
||||
|
tags.remove(htmlTag); |
||||
|
return tags.toArray(new HtmlTag[0]); |
||||
|
} |
||||
|
|
||||
|
private static boolean isHtmlTagSupported(String name, String attribute, String attributeValue) { |
||||
|
return name.equals("b") || name.equals("i") || name.equals("span") || name.equals("del") || name.equals("a"); |
||||
|
} |
||||
|
|
||||
|
private static boolean isBBTagSupported(String name) { |
||||
|
return name.equals("b") || name.equals("i") || name.equals("u") || name.equals("s"); |
||||
|
} |
||||
|
|
||||
|
private static boolean isHtmlTag(String tagName) { |
||||
|
for (String tag : ALL_HTML_TAGS) |
||||
|
if (TextUtils.equals(tag, tagName)) return true; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static boolean containsHtml(String s) { |
||||
|
return getHtmlTags(s).length > 0; |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
package gr.thmmy.mthmmy.viewmodel; |
||||
|
|
||||
|
import android.os.AsyncTask; |
||||
|
|
||||
|
import androidx.lifecycle.MutableLiveData; |
||||
|
import androidx.lifecycle.ViewModel; |
||||
|
import gr.thmmy.mthmmy.activities.shoutbox.SendShoutTask; |
||||
|
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxTask; |
||||
|
import gr.thmmy.mthmmy.model.Shoutbox; |
||||
|
import gr.thmmy.mthmmy.session.SessionManager; |
||||
|
|
||||
|
public class ShoutboxViewModel extends ViewModel { |
||||
|
private MutableLiveData<Shoutbox> shoutboxMutableLiveData = new MutableLiveData<>(); |
||||
|
private ShoutboxTask shoutboxTask; |
||||
|
private ShoutboxTask.OnTaskStartedListener onShoutboxTaskStarted; |
||||
|
private ShoutboxTask.OnNetworkTaskFinishedListener<Shoutbox> onShoutboxTaskFinished; |
||||
|
private SendShoutTask.OnTaskStartedListener onSendShoutTaskStarted; |
||||
|
private SendShoutTask.OnNetworkTaskFinishedListener<Void> onSendShoutTaskFinished; |
||||
|
|
||||
|
public void loadShoutbox() { |
||||
|
if (shoutboxTask != null && shoutboxTask.getStatus() == AsyncTask.Status.RUNNING) |
||||
|
shoutboxTask.cancel(true); |
||||
|
shoutboxTask = new ShoutboxTask(onShoutboxTaskStarted, onShoutboxTaskFinished); |
||||
|
shoutboxTask.execute(SessionManager.shoutboxUrl.toString()); |
||||
|
} |
||||
|
|
||||
|
public void sendShout(String shout) { |
||||
|
if (shoutboxMutableLiveData.getValue() == null) throw new IllegalStateException("Shoutbox task has not finished yet!"); |
||||
|
Shoutbox shoutbox = shoutboxMutableLiveData.getValue(); |
||||
|
new SendShoutTask(onSendShoutTaskStarted, onSendShoutTaskFinished) |
||||
|
.execute(shoutbox.getSendShoutUrl(), shout, shoutbox.getSc(), |
||||
|
shoutbox.getShoutName(), shoutbox.getShoutSend(), shoutbox.getShoutUrl()); |
||||
|
} |
||||
|
|
||||
|
public void setShoutbox(Shoutbox shoutbox) { |
||||
|
shoutboxMutableLiveData.setValue(shoutbox); |
||||
|
} |
||||
|
|
||||
|
public MutableLiveData<Shoutbox> getShoutboxMutableLiveData() { |
||||
|
return shoutboxMutableLiveData; |
||||
|
} |
||||
|
|
||||
|
public void setOnSendShoutTaskFinished(SendShoutTask.OnNetworkTaskFinishedListener<Void> onSendShoutTaskFinished) { |
||||
|
this.onSendShoutTaskFinished = onSendShoutTaskFinished; |
||||
|
} |
||||
|
|
||||
|
public void setOnSendShoutTaskStarted(SendShoutTask.OnTaskStartedListener onSendShoutTaskStarted) { |
||||
|
this.onSendShoutTaskStarted = onSendShoutTaskStarted; |
||||
|
} |
||||
|
|
||||
|
public void setOnShoutboxTaskFinished(ShoutboxTask.OnNetworkTaskFinishedListener<Shoutbox> onShoutboxTaskFinished) { |
||||
|
this.onShoutboxTaskFinished = onShoutboxTaskFinished; |
||||
|
} |
||||
|
|
||||
|
public void setOnShoutboxTaskStarted(ShoutboxTask.OnTaskStartedListener onShoutboxTaskStarted) { |
||||
|
this.onShoutboxTaskStarted = onShoutboxTaskStarted; |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
|
||||
|
<item android:color="@color/accent" android:state_selected="true" /> |
||||
|
<item android:color="@color/white" android:state_selected="false" /> |
||||
|
</selector> |
@ -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="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/> |
||||
|
</vector> |
@ -0,0 +1,7 @@ |
|||||
|
<vector android:height="24dp" android:viewportHeight="297" |
||||
|
android:viewportWidth="297" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:tint="#FFFFFF"> |
||||
|
<path android:fillColor="#FF000000" |
||||
|
android:pathData="M282.82,107.559h-25.792V10.025c0,-4.056 -2.443,-7.711 -6.19,-9.261c-3.745,-1.555 -8.059,-0.693 -10.925,2.177C195.46,47.434 157.039,68.643 132.635,78.6C106.33,89.333 90.261,89.635 90.159,89.637H14.18c-5.536,0 -10.023,4.488 -10.023,10.024v89.631c0,5.536 4.487,10.023 10.023,10.023h10.2l37.887,91.497c1.551,3.746 5.206,6.189 9.261,6.189h55.538c0.006,-0.001 0.012,-0.001 0.02,0c5.536,0 10.023,-4.488 10.023,-10.023c0,-1.588 -0.37,-3.09 -1.025,-4.424l-33.809,-81.646c22.4,4.443 73.884,21.285 137.641,85.1c1.917,1.921 4.483,2.939 7.094,2.939c0.055,0 0.109,0 0.164,0c5.468,-0.079 9.877,-4.536 9.877,-10.023c0,-0.214 -0.006,-0.428 -0.02,-0.639l-0.002,-96.896h25.792c5.536,0 10.023,-4.488 10.023,-10.024v-53.779C292.844,112.048 288.356,107.559 282.82,107.559zM24.204,109.683h55.932v69.584H24.204V109.683zM78.226,276.952l-31.139,-75.196h33.839l31.138,75.196H78.226zM100.183,180.201v-71.452c20.889,-3.123 72.28,-16.674 136.797,-75.301v84.121v0.015v53.779c0,0.008 0,0.017 0,0.025l0.002,84.111C172.466,196.876 121.072,183.326 100.183,180.201zM272.796,161.34h-15.768v-33.732h15.768V161.34z" |
||||
|
android:strokeColor="#000000" android:strokeWidth="1"/> |
||||
|
</vector> |
@ -1,5 +1,5 @@ |
|||||
<vector android:height="24dp" android:tint="@color/accent" |
<vector android:height="48dp" android:tint="@color/accent" |
||||
android:viewportHeight="24.0" android:viewportWidth="24.0" |
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
android:width="30dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<path android:fillColor="#FF000000" android:pathData="M7,10l5,5 5,-5z"/> |
<path android:fillColor="#FF000000" android:pathData="M7,10l5,5 5,-5z"/> |
||||
</vector> |
</vector> |
||||
|
@ -1,5 +1,5 @@ |
|||||
<vector android:height="24dp" android:tint="@color/accent" |
<vector android:height="48dp" android:tint="@color/accent" |
||||
android:viewportHeight="24.0" android:viewportWidth="24.0" |
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
android:width="30dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<path android:fillColor="#FF000000" android:pathData="M7,14l5,-5 5,5z"/> |
<path android:fillColor="#FF000000" android:pathData="M7,14l5,-5 5,5z"/> |
||||
</vector> |
</vector> |
||||
|
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="24dp" android:viewportHeight="512" |
||||
|
android:viewportWidth="512" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#ffffff" android:pathData="M256,60c-108.27,0 -196,87.73 -196,196c0,108.27 87.73,196 196,196c108.27,0 196,-87.73 196,-196c0,-108.27 -87.73,-196 -196,-196z"/> |
||||
|
<path android:fillColor="#3c3c3c" android:pathData="M504,256c0,137 -111,248 -248,248c-137,0 -248,-111 -248,-248c0,-137 111,-248 248,-248c137,0 248,111 248,248zM168,192c0,48.6 39.4,88 88,88c48.6,0 88,-39.4 88,-88c0,-48.6 -39.4,-88 -88,-88c-48.6,0 -88,39.4 -88,88zM402.5,379.8c-18.8,-35.4 -55.6,-59.8 -98.5,-59.8c-2.4,0 -4.8,0.4 -7.1,1.1c-12.9,4.2 -26.6,6.9 -40.9,6.9c-14.3,0 -27.9,-2.7 -40.9,-6.9c-2.3,-0.7 -4.7,-1.1 -7.1,-1.1c-42.9,0 -79.7,24.4 -98.5,59.8c35.2,41.6 87.8,68.2 146.5,68.2c58.7,0 111.3,-26.6 146.5,-68.2z"/> |
||||
|
</vector> |
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="24dp" android:viewportHeight="512" |
||||
|
android:viewportWidth="512" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#ffffff" android:pathData="M256,60c-108.27,0 -196,87.73 -196,196c0,108.27 87.73,196 196,196c108.27,0 196,-87.73 196,-196c0,-108.27 -87.73,-196 -196,-196z"/> |
||||
|
<path android:fillColor="#323232" android:pathData="M504,256c0,137 -111,248 -248,248c-137,0 -248,-111 -248,-248c0,-137 111,-248 248,-248c137,0 248,111 248,248zM168,192c0,48.6 39.4,88 88,88c48.6,0 88,-39.4 88,-88c0,-48.6 -39.4,-88 -88,-88c-48.6,0 -88,39.4 -88,88zM402.5,379.8c-18.8,-35.4 -55.6,-59.8 -98.5,-59.8c-2.4,0 -4.8,0.4 -7.1,1.1c-12.9,4.2 -26.6,6.9 -40.9,6.9c-14.3,0 -27.9,-2.7 -40.9,-6.9c-2.3,-0.7 -4.7,-1.1 -7.1,-1.1c-42.9,0 -79.7,24.4 -98.5,59.8c35.2,41.6 87.8,68.2 146.5,68.2c58.7,0 111.3,-26.6 146.5,-68.2z"/> |
||||
|
</vector> |
@ -1,5 +0,0 @@ |
|||||
<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="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> |
|
||||
</vector> |
|
@ -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,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM8.5,15L7.3,15l-2.55,-3.5L4.75,15L3.5,15L3.5,9h1.25l2.5,3.5L7.25,9L8.5,9v6zM13.5,10.26L11,10.26v1.12h2.5v1.26L11,12.64v1.11h2.5L13.5,15h-4L9.5,9h4v1.26zM20.5,14c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1L14.5,9h1.25v4.51h1.13L16.88,9.99h1.25v3.51h1.12L19.25,9h1.25v5z"/> |
||||
|
</vector> |
@ -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="M21,6h-2v9L6,15v2c0,0.55 0.45,1 1,1h11l4,4L22,7c0,-0.55 -0.45,-1 -1,-1zM17,12L17,3c0,-0.55 -0.45,-1 -1,-1L3,2c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/> |
||||
|
</vector> |
@ -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="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/> |
||||
|
</vector> |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue