diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java index 2cd848d0e..0a217c9f5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java @@ -61,6 +61,7 @@ public class AccountActivity extends BaseActivity { private boolean isSelf; private String openInWebUrl; private TabLayout tabLayout; + private Account loadedAccount; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -170,6 +171,8 @@ public class AccountActivity extends BaseActivity { } private void onObtainAccountSuccess(Account account) { + loadedAccount = account; + TextView username = (TextView) findViewById(R.id.account_username); TextView displayName = (TextView) findViewById(R.id.account_display_name); TextView note = (TextView) findViewById(R.id.account_note); @@ -180,7 +183,7 @@ public class AccountActivity extends BaseActivity { getString(R.string.status_username_format), account.username); username.setText(usernameFormatted); - displayName.setText(account.displayName); + displayName.setText(account.getDisplayName()); note.setText(account.note); note.setLinksClickable(true); @@ -448,6 +451,12 @@ public class AccountActivity extends BaseActivity { onBackPressed(); return true; } + case R.id.action_mention: { + Intent intent = new Intent(this, ComposeActivity.class); + intent.putExtra("mentioned_usernames", new String[] { loadedAccount.username }); + startActivity(intent); + return true; + } case R.id.action_open_in_web: { Uri uri = Uri.parse(openInWebUrl); Intent intent = new Intent(Intent.ACTION_VIEW, uri); diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java index 739cec35c..52f64a52e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java @@ -105,7 +105,7 @@ class BlocksAdapter extends AccountAdapter { void setupWithAccount(Account account) { id = account.id; - displayName.setText(account.displayName); + displayName.setText(account.getDisplayName()); String format = username.getContext().getString(R.string.status_username_format); String formattedUsername = String.format(format, account.username); username.setText(formattedUsername); diff --git a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java index 6512aff24..d15708cfd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java @@ -95,7 +95,7 @@ class FollowAdapter extends AccountAdapter { String format = username.getContext().getString(R.string.status_username_format); String formattedUsername = String.format(format, account.username); username.setText(formattedUsername); - displayName.setText(account.displayName); + displayName.setText(account.getDisplayName()); note.setText(account.note); Context context = avatar.getContext(); Picasso.with(context) diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 443fecd59..226264337 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.SystemClock; @@ -28,10 +29,18 @@ import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; import android.support.v4.view.ViewPager; import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; import android.view.View; import android.widget.ImageView; +import android.widget.TextView; import com.arlib.floatingsearchview.FloatingSearchView; +import com.arlib.floatingsearchview.suggestions.SearchSuggestionsAdapter; +import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion; import com.keylesspalace.tusky.entity.Account; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.materialdrawer.AccountHeader; @@ -43,14 +52,17 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; +import com.mikepenz.materialdrawer.model.interfaces.IProfile; import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader; import com.mikepenz.materialdrawer.util.DrawerImageLoader; import com.squareup.picasso.Picasso; +import java.util.List; import java.util.Stack; import retrofit2.Call; import retrofit2.Callback; +import retrofit2.Response; public class MainActivity extends BaseActivity { private static final String TAG = "MainActivity"; // logging tag and Volley request tag @@ -59,7 +71,8 @@ public class MainActivity extends BaseActivity { private PendingIntent serviceAlarmIntent; private boolean notificationServiceEnabled; private String loggedInAccountId; - Stack pageHistory = new Stack<>(); + private String loggedInAccountUsername; + Stack pageHistory = new Stack(); private ViewPager viewPager; private AccountHeader headerResult; private Drawer drawer; @@ -126,13 +139,9 @@ public class MainActivity extends BaseActivity { long drawerItemIdentifier = drawerItem.getIdentifier(); if (drawerItemIdentifier == 0) { - if (loggedInAccountId != null) { - Intent intent = new Intent(MainActivity.this, AccountActivity.class); - intent.putExtra("id", loggedInAccountId); - startActivity(intent); - } else { - Log.e(TAG, "Logged-in account id was not obtained yet when profile was opened."); - } + Intent intent = new Intent(MainActivity.this, AccountActivity.class); + intent.putExtra("id", loggedInAccountId); + startActivity(intent); } else if (drawerItemIdentifier == 1) { Intent intent = new Intent(MainActivity.this, FavouritesActivity.class); startActivity(intent); @@ -165,12 +174,76 @@ public class MainActivity extends BaseActivity { searchView.attachNavigationDrawerToMenuButton(drawer.getDrawerLayout()); + searchView.setOnQueryChangeListener(new FloatingSearchView.OnQueryChangeListener() { + @Override + public void onSearchTextChanged(String oldQuery, String newQuery) { + if (!oldQuery.equals("") && newQuery.equals("")) { + searchView.clearSuggestions(); + return; + } + + if (newQuery.length() < 3) { + return; + } + + searchView.showProgress(); + + mastodonAPI.searchAccounts(newQuery, false, 5).enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + searchView.swapSuggestions(response.body()); + searchView.hideProgress(); + } + + @Override + public void onFailure(Call> call, Throwable t) { + searchView.hideProgress(); + } + }); + } + }); + + searchView.setOnSearchListener(new FloatingSearchView.OnSearchListener() { + @Override + public void onSuggestionClicked(SearchSuggestion searchSuggestion) { + Account accountSuggestion = (Account) searchSuggestion; + Intent intent = new Intent(MainActivity.this, AccountActivity.class); + intent.putExtra("id", accountSuggestion.id); + startActivity(intent); + } + + @Override + public void onSearchAction(String currentQuery) { + + } + }); + + searchView.setOnBindSuggestionCallback(new SearchSuggestionsAdapter.OnBindSuggestionCallback() { + @Override + public void onBindSuggestion(View suggestionView, ImageView leftIcon, TextView textView, SearchSuggestion item, int itemPosition) { + Account accountSuggestion = ((Account) item); + + Picasso.with(MainActivity.this) + .load(accountSuggestion.avatar) + .placeholder(R.drawable.avatar_default) + .into(leftIcon); + + String searchStr = accountSuggestion.getDisplayName() + " " + accountSuggestion.username; + final SpannableStringBuilder str = new SpannableStringBuilder(searchStr); + + str.setSpan(new android.text.style.StyleSpan(Typeface.BOLD), 0, accountSuggestion.getDisplayName().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + textView.setText(str); + textView.setMaxLines(1); + textView.setEllipsize(TextUtils.TruncateAt.END); + } + }); + // Setup the tabs and timeline pager. TimelinePagerAdapter adapter = new TimelinePagerAdapter(getSupportFragmentManager()); String[] pageTitles = { - getString(R.string.title_home), - getString(R.string.title_notifications), - getString(R.string.title_public) + getString(R.string.title_home), + getString(R.string.title_notifications), + getString(R.string.title_public) }; adapter.setPageTitles(pageTitles); viewPager = (ViewPager) findViewById(R.id.pager); @@ -234,6 +307,13 @@ public class MainActivity extends BaseActivity { SharedPreferences preferences = getSharedPreferences( getString(R.string.preferences_file_key), Context.MODE_PRIVATE); final String domain = preferences.getString("domain", null); + String id = preferences.getString("loggedInAccountId", null); + String username = preferences.getString("loggedInAccountUsername", null); + + if (id != null && username != null) { + loggedInAccountId = id; + loggedInAccountUsername = username; + } mastodonAPI.accountVerifyCredentials().enqueue(new Callback() { @Override @@ -250,12 +330,12 @@ public class MainActivity extends BaseActivity { headerResult.addProfiles( new ProfileDrawerItem() - .withName(me.displayName) + .withName(me.getDisplayName()) .withEmail(String.format("%s@%s", me.username, domain)) .withIcon(me.avatar) ); - loggedInAccountId = me.id; + onFetchUserInfoSuccess(me.id, me.username); } @Override @@ -265,6 +345,17 @@ public class MainActivity extends BaseActivity { }); } + private void onFetchUserInfoSuccess(String id, String username) { + loggedInAccountId = id; + loggedInAccountUsername = username; + SharedPreferences preferences = getSharedPreferences( + getString(R.string.preferences_file_key), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("loggedInAccountId", loggedInAccountId); + editor.putString("loggedInAccountUsername", loggedInAccountUsername); + editor.apply(); + } + private void onFetchUserInfoFailure(Exception exception) { Log.e(TAG, "Failed to fetch user info. " + exception.getMessage()); } @@ -280,4 +371,4 @@ public class MainActivity extends BaseActivity { viewPager.setCurrentItem(pageHistory.lastElement()); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java index 630b95686..212a4b993 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java @@ -96,13 +96,13 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe case FAVOURITE: case REBLOG: { StatusNotificationViewHolder holder = (StatusNotificationViewHolder) viewHolder; - holder.setMessage(type, notification.account.displayName, + holder.setMessage(type, notification.account.getDisplayName(), notification.status); break; } case FOLLOW: { FollowViewHolder holder = (FollowViewHolder) viewHolder; - holder.setMessage(notification.account.displayName, notification.account.username, + holder.setMessage(notification.account.getDisplayName(), notification.account.username, notification.account.avatar); holder.setupButtons(followListener, notification.account.id); break; diff --git a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java b/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java index 44fc636fe..745e03711 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java +++ b/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java @@ -119,7 +119,7 @@ public class PullNotificationService extends IntentService { if (status != null) { MentionResult mention = new MentionResult(); mention.content = status.content.toString(); - mention.displayName = notification.account.displayName; + mention.displayName = notification.account.getDisplayName(); mention.avatarUrl = status.account.avatar; mentions.add(mention); } diff --git a/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java b/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java index e53601c52..3f4b5c0b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java @@ -17,6 +17,7 @@ package com.keylesspalace.tusky; import android.content.Intent; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; @@ -25,6 +26,8 @@ import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -45,6 +48,11 @@ public class ReportActivity extends BaseActivity { private View anyView; // what Snackbar will use to find the root view private ReportAdapter adapter; private boolean reportAlreadyInFlight; + private String accountId; + private String accountUsername; + private String statusId; + private String statusContent; + private EditText comment; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -52,10 +60,10 @@ public class ReportActivity extends BaseActivity { setContentView(R.layout.activity_report); Intent intent = getIntent(); - final String accountId = intent.getStringExtra("account_id"); - String accountUsername = intent.getStringExtra("account_username"); - String statusId = intent.getStringExtra("status_id"); - String statusContent = intent.getStringExtra("status_content"); + accountId = intent.getStringExtra("account_id"); + accountUsername = intent.getStringExtra("account_username"); + statusId = intent.getStringExtra("status_id"); + statusContent = intent.getStringExtra("status_content"); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -64,6 +72,8 @@ public class ReportActivity extends BaseActivity { String title = String.format(getString(R.string.report_username_format), accountUsername); bar.setTitle(title); + bar.setDisplayHomeAsUpEnabled(true); + bar.setDisplayShowHomeEnabled(true); } anyView = toolbar; @@ -85,28 +95,28 @@ public class ReportActivity extends BaseActivity { HtmlUtils.fromHtml(statusContent), true); adapter.addItem(reportStatus); - final EditText comment = (EditText) findViewById(R.id.report_comment); - Button send = (Button) findViewById(R.id.report_send); + comment = (EditText) findViewById(R.id.report_comment); + reportAlreadyInFlight = false; - send.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (reportAlreadyInFlight) { - return; - } - String[] statusIds = adapter.getCheckedStatusIds(); - if (statusIds.length > 0) { - reportAlreadyInFlight = true; - sendReport(accountId, statusIds, comment.getText().toString()); - } else { - comment.setError(getString(R.string.error_report_too_few_statuses)); - } - } - }); fetchRecentStatuses(accountId); } + private void onClickSend() { + if (reportAlreadyInFlight) { + return; + } + + String[] statusIds = adapter.getCheckedStatusIds(); + + if (statusIds.length > 0) { + reportAlreadyInFlight = true; + sendReport(accountId, statusIds, comment.getText().toString()); + } else { + comment.setError(getString(R.string.error_report_too_few_statuses)); + } + } + private void sendReport(final String accountId, final String[] statusIds, final String comment) { mastodonAPI.report(accountId, Arrays.asList(statusIds), comment).enqueue(new Callback() { @@ -167,4 +177,25 @@ public class ReportActivity extends BaseActivity { private void onFetchStatusesFailure(Exception exception) { Log.e(TAG, "Failed to fetch recent statuses to report. " + exception.getMessage()); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.report_toolbar, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: { + onBackPressed(); + return true; + } + case R.id.action_report: { + onClickSend(); + return true; + } + } + return super.onOptionsItemSelected(item); + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java index c64f669fa..35168d048 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java @@ -343,14 +343,14 @@ class StatusViewHolder extends RecyclerView.ViewHolder { void setupWithStatus(Status status, StatusActionListener listener) { Status realStatus = status.getActionableStatus(); - setDisplayName(realStatus.account.displayName); + setDisplayName(realStatus.account.getDisplayName()); setUsername(realStatus.account.username); setCreatedAt(realStatus.createdAt); setContent(realStatus.content, realStatus.mentions, listener); setAvatar(realStatus.account.avatar); setReblogged(realStatus.reblogged); setFavourited(realStatus.favourited); - String rebloggedByDisplayName = status.account.displayName; + String rebloggedByDisplayName = status.account.getDisplayName(); if (status.reblog == null) { hideRebloggedByDisplayName(); } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Account.java b/app/src/main/java/com/keylesspalace/tusky/entity/Account.java index d091512ef..76574b940 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Account.java +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Account.java @@ -15,13 +15,18 @@ package com.keylesspalace.tusky.entity; +import android.os.Parcel; import android.text.Spanned; +import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion; import com.google.gson.annotations.SerializedName; -public class Account { +public class Account implements SearchSuggestion { public String id; + @SerializedName("username") + public String localUsername; + @SerializedName("acct") public String username; @@ -62,4 +67,47 @@ public class Account { Account account = (Account) other; return account.id.equals(this.id); } + + public String getDisplayName() { + if (displayName.length() == 0) { + return localUsername; + } + + return displayName; + } + + @Override + public String getBody() { + return username; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + + } + + public Account() { + + } + + protected Account(Parcel in) { + + } + + public static final Creator CREATOR = new Creator() { + @Override + public Account createFromParcel(Parcel source) { + return new Account(source); + } + + @Override + public Account[] newArray(int size) { + return new Account[size]; + } + }; } diff --git a/app/src/main/res/layout/activity_report.xml b/app/src/main/res/layout/activity_report.xml index 9b884f32a..c9f89a22b 100644 --- a/app/src/main/res/layout/activity_report.xml +++ b/app/src/main/res/layout/activity_report.xml @@ -1,51 +1,43 @@ - - - + android:layout_height="match_parent" + android:orientation="vertical"> + - + - - - - -