diff --git a/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java b/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java index 01d98122d..ca446945d 100644 --- a/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java +++ b/app/src/fdroid/java/com/keylesspalace/tusky/MessagingService.java @@ -21,12 +21,17 @@ import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.Spanned; -import android.util.ArraySet; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.keylesspalace.tusky.entity.Notification; +import com.keylesspalace.tusky.json.SpannedTypeAdapter; +import com.keylesspalace.tusky.json.StringWithEmoji; +import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.util.NotificationMaker; +import com.keylesspalace.tusky.util.OkHttpUtils; import java.util.HashSet; import java.util.List; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a286e60e4..b5724943e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -79,11 +79,11 @@ android:name="com.theartofdev.edmodo.cropper.CropImageActivity" android:theme="@style/Base.Theme.AppCompat" /> - + diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java index ed12bd2cb..f3a83f4d0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.java @@ -23,6 +23,7 @@ import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.annotation.AttrRes; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; @@ -43,6 +44,14 @@ import android.widget.TextView; import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Relationship; +import com.keylesspalace.tusky.fragment.SFragment; +import com.keylesspalace.tusky.interfaces.LinkListener; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; +import com.keylesspalace.tusky.pager.AccountPagerAdapter; +import com.keylesspalace.tusky.util.LinkHelper; +import com.keylesspalace.tusky.util.Assert; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; @@ -237,7 +246,9 @@ public class AccountActivity extends BaseActivity implements SFragment.OnUserRem displayName.setText(account.getDisplayName()); - LinkHelper.setClickableText(note, account.note, null, new LinkListener() { + boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this) + .getBoolean("customTabs", true); + LinkHelper.setClickableText(note, account.note, null, useCustomTabs, new LinkListener() { @Override public void onViewTag(String tag) { Intent intent = new Intent(AccountActivity.this, ViewTagActivity.class); diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java index 1d1276fbc..86c5a05c3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java @@ -24,6 +24,8 @@ import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.MenuItem; +import com.keylesspalace.tusky.fragment.AccountListFragment; + public class AccountListActivity extends BaseActivity { enum Type { BLOCKS, diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 5bbe7ba13..1803cacd1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -34,6 +34,13 @@ import android.view.Menu; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.keylesspalace.tusky.json.SpannedTypeAdapter; +import com.keylesspalace.tusky.json.StringWithEmoji; +import com.keylesspalace.tusky.json.StringWithEmojiTypeAdapter; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.network.TuskyAPI; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.OkHttpUtils; import java.io.IOException; @@ -51,7 +58,7 @@ import retrofit2.converter.gson.GsonConverterFactory; public class BaseActivity extends AppCompatActivity { private static final String TAG = "BaseActivity"; // logging tag - protected MastodonAPI mastodonAPI; + public MastodonAPI mastodonAPI; protected TuskyAPI tuskyAPI; protected Dispatcher mastodonApiDispatcher; protected PendingIntent serviceAlarmIntent; diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 15fefa90d..f0089fd2f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -71,6 +71,14 @@ import android.widget.TextView; import com.keylesspalace.tusky.entity.Media; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.fragment.ComposeOptionsFragment; +import com.keylesspalace.tusky.util.DownsizeImageTask; +import com.keylesspalace.tusky.util.EditTextTyped; +import com.keylesspalace.tusky.util.CountUpDownLatch; +import com.keylesspalace.tusky.util.IOUtils; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.SpanUtils; +import com.keylesspalace.tusky.util.ThemeUtils; import java.io.ByteArrayOutputStream; import java.io.File; @@ -119,7 +127,8 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFrag private Uri photoUploadUri; // this only exists when a status is trying to be sent, but uploads are still occurring private ProgressDialog finishingUploadDialog; - @BindView(R.id.compose_edit_field) EditTextTyped textEditor; + @BindView(R.id.compose_edit_field) + EditTextTyped textEditor; @BindView(R.id.compose_media_preview_bar) LinearLayout mediaPreviewBar; @BindView(R.id.compose_content_warning_bar) View contentWarningBar; @BindView(R.id.field_content_warning) EditText contentWarningEditor; diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java index ddb538083..23c633b63 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.java @@ -36,7 +36,6 @@ import android.util.Base64; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ImageView; @@ -44,6 +43,8 @@ import android.widget.ProgressBar; import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Profile; +import com.keylesspalace.tusky.util.IOUtils; +import com.keylesspalace.tusky.util.Log; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; import com.theartofdev.edmodo.cropper.CropImage; diff --git a/app/src/main/java/com/keylesspalace/tusky/FavouritesActivity.java b/app/src/main/java/com/keylesspalace/tusky/FavouritesActivity.java index f45ab2c6c..7755f2177 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FavouritesActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/FavouritesActivity.java @@ -23,6 +23,10 @@ import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.MenuItem; +import com.keylesspalace.tusky.fragment.SFragment; +import com.keylesspalace.tusky.fragment.TimelineFragment; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; + public class FavouritesActivity extends BaseActivity implements SFragment.OnUserRemovedListener { private StatusRemoveListener statusRemoveListener; diff --git a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java index b85f1faaa..8ec477484 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/LoginActivity.java @@ -36,6 +36,10 @@ import android.widget.TextView; import com.keylesspalace.tusky.entity.AccessToken; import com.keylesspalace.tusky.entity.AppCredentials; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.util.CustomTabsHelper; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.OkHttpUtils; import java.util.HashMap; import java.util.Map; diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 764d1bddf..c93bb5ed6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -42,6 +42,11 @@ 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.keylesspalace.tusky.fragment.SFragment; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; +import com.keylesspalace.tusky.pager.TimelinePagerAdapter; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.materialdrawer.AccountHeader; import com.mikepenz.materialdrawer.AccountHeaderBuilder; @@ -82,7 +87,7 @@ public class MainActivity extends BaseActivity implements SFragment.OnUserRemove @BindView(R.id.tab_layout) TabLayout tabLayout; @BindView(R.id.pager) ViewPager viewPager; - FloatingActionButton composeButton; + public FloatingActionButton composeButton; @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java index 53e4f22a4..58c185881 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java @@ -21,6 +21,8 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; +import com.keylesspalace.tusky.fragment.PreferencesFragment; + public class PreferencesActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener { private boolean themeSwitched; diff --git a/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java b/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java index 0c3d1895f..f8e4e4472 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ReportActivity.java @@ -30,7 +30,11 @@ import android.view.MenuItem; import android.view.View; import android.widget.EditText; +import com.keylesspalace.tusky.adapter.ReportAdapter; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.util.HtmlUtils; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import java.util.ArrayList; import java.util.Arrays; diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java index 9bce54a14..e73d490a2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java @@ -23,6 +23,10 @@ import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.MenuItem; +import com.keylesspalace.tusky.fragment.SFragment; +import com.keylesspalace.tusky.fragment.TimelineFragment; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; + import butterknife.BindView; import butterknife.ButterKnife; diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewThreadActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewThreadActivity.java index 160e438f6..20b3534ac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewThreadActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewThreadActivity.java @@ -25,6 +25,10 @@ import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; +import com.keylesspalace.tusky.fragment.SFragment; +import com.keylesspalace.tusky.fragment.ViewThreadFragment; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; + public class ViewThreadActivity extends BaseActivity implements SFragment.OnUserRemovedListener { Fragment viewThreadFragment; diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java similarity index 87% rename from app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java index 9941d34d3..48c6634bf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java @@ -13,17 +13,18 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import com.keylesspalace.tusky.entity.Account; +import com.keylesspalace.tusky.interfaces.AccountActionListener; import java.util.ArrayList; import java.util.List; -abstract class AccountAdapter extends RecyclerView.Adapter { +public abstract class AccountAdapter extends RecyclerView.Adapter { List accountList; AccountActionListener accountActionListener; @@ -38,7 +39,7 @@ abstract class AccountAdapter extends RecyclerView.Adapter { return accountList.size() + 1; } - void update(List newAccounts) { + public void update(List newAccounts) { if (newAccounts == null || newAccounts.isEmpty()) { return; } @@ -59,14 +60,14 @@ abstract class AccountAdapter extends RecyclerView.Adapter { notifyDataSetChanged(); } - void addItems(List newAccounts) { + public void addItems(List newAccounts) { int end = accountList.size(); accountList.addAll(newAccounts); notifyItemRangeInserted(end, newAccounts.size()); } @Nullable - Account removeItem(int position) { + public Account removeItem(int position) { if (position < 0 || position >= accountList.size()) { return null; } @@ -75,7 +76,7 @@ abstract class AccountAdapter extends RecyclerView.Adapter { return account; } - void addItem(Account account, int position) { + public void addItem(Account account, int position) { if (position < 0 || position > accountList.size()) { return; } diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java similarity index 94% rename from app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java index d052db34b..e4e34fa9b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -22,18 +22,20 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Account; +import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; import butterknife.BindView; import butterknife.ButterKnife; -class BlocksAdapter extends AccountAdapter { +public class BlocksAdapter extends AccountAdapter { private static final int VIEW_TYPE_BLOCKED_USER = 0; private static final int VIEW_TYPE_FOOTER = 1; - BlocksAdapter(AccountActionListener accountActionListener) { + public BlocksAdapter(AccountActionListener accountActionListener) { super(accountActionListener); } diff --git a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java similarity index 93% rename from app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java index 3b09de247..7e9b825fa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; @@ -22,16 +22,18 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Account; +import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; /** Both for follows and following lists. */ -class FollowAdapter extends AccountAdapter { +public class FollowAdapter extends AccountAdapter { private static final int VIEW_TYPE_ACCOUNT = 0; private static final int VIEW_TYPE_FOOTER = 1; - FollowAdapter(AccountActionListener accountActionListener) { + public FollowAdapter(AccountActionListener accountActionListener) { super(accountActionListener); } diff --git a/app/src/main/java/com/keylesspalace/tusky/FollowRequestsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java similarity index 94% rename from app/src/main/java/com/keylesspalace/tusky/FollowRequestsAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java index 0a54b558b..392d33ac7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FollowRequestsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -22,6 +22,8 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; +import com.keylesspalace.tusky.interfaces.AccountActionListener; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Account; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; @@ -29,11 +31,11 @@ import com.squareup.picasso.Picasso; import butterknife.BindView; import butterknife.ButterKnife; -class FollowRequestsAdapter extends AccountAdapter { +public class FollowRequestsAdapter extends AccountAdapter { private static final int VIEW_TYPE_FOLLOW_REQUEST = 0; private static final int VIEW_TYPE_FOOTER = 1; - FollowRequestsAdapter(AccountActionListener accountActionListener) { + public FollowRequestsAdapter(AccountActionListener accountActionListener) { super(accountActionListener); } diff --git a/app/src/main/java/com/keylesspalace/tusky/FooterViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FooterViewHolder.java similarity index 93% rename from app/src/main/java/com/keylesspalace/tusky/FooterViewHolder.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/FooterViewHolder.java index e39ca44af..5ff1187e7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FooterViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FooterViewHolder.java @@ -13,12 +13,14 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ProgressBar; +import com.keylesspalace.tusky.R; + class FooterViewHolder extends RecyclerView.ViewHolder { FooterViewHolder(View itemView) { super(itemView); diff --git a/app/src/main/java/com/keylesspalace/tusky/MutesAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java similarity index 93% rename from app/src/main/java/com/keylesspalace/tusky/MutesAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java index 2513cac6b..06258a4bc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MutesAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java @@ -1,4 +1,4 @@ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -7,21 +7,20 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Account; +import com.keylesspalace.tusky.interfaces.AccountActionListener; import com.pkmmte.view.CircularImageView; import com.squareup.picasso.Picasso; -import java.util.HashSet; -import java.util.Set; - import butterknife.BindView; import butterknife.ButterKnife; -class MutesAdapter extends AccountAdapter { +public class MutesAdapter extends AccountAdapter { private static final int VIEW_TYPE_MUTED_USER = 0; private static final int VIEW_TYPE_FOOTER = 1; - MutesAdapter(AccountActionListener accountActionListener) { + public MutesAdapter(AccountActionListener accountActionListener) { super(accountActionListener); } diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java similarity index 95% rename from app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index 2376469b5..639bfea37 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.content.Context; import android.graphics.Typeface; @@ -29,21 +29,24 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Notification; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.AdapterItemRemover; +import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.squareup.picasso.Picasso; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRemover { +public class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRemover { private static final int VIEW_TYPE_MENTION = 0; private static final int VIEW_TYPE_FOOTER = 1; private static final int VIEW_TYPE_STATUS_NOTIFICATION = 2; private static final int VIEW_TYPE_FOLLOW = 3; - enum FooterState { + public enum FooterState { EMPTY, END, LOADING @@ -54,7 +57,7 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe private NotificationActionListener notificationActionListener; private FooterState footerState = FooterState.END; - NotificationsAdapter(StatusActionListener statusListener, + public NotificationsAdapter(StatusActionListener statusListener, NotificationActionListener notificationActionListener) { super(); notifications = new ArrayList<>(); @@ -63,7 +66,7 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe } - void setFooterState(FooterState newFooterState) { + public void setFooterState(FooterState newFooterState) { FooterState oldValue = footerState; footerState = newFooterState; if (footerState != oldValue) { @@ -179,7 +182,7 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe return null; } - void update(List newNotifications) { + public void update(List newNotifications) { if (newNotifications == null || newNotifications.isEmpty()) { return; } @@ -200,7 +203,7 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe notifyDataSetChanged(); } - void addItems(List new_notifications) { + public void addItems(List new_notifications) { int end = notifications.size(); notifications.addAll(new_notifications); notifyItemRangeInserted(end, new_notifications.size()); @@ -223,7 +226,7 @@ class NotificationsAdapter extends RecyclerView.Adapter implements AdapterItemRe } } - interface NotificationActionListener { + public interface NotificationActionListener { void onViewAccount(String id); } diff --git a/app/src/main/java/com/keylesspalace/tusky/ReportAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ReportAdapter.java similarity index 90% rename from app/src/main/java/com/keylesspalace/tusky/ReportAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/ReportAdapter.java index ccdf7aa9b..50e07369d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ReportAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ReportAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.text.Spanned; @@ -24,16 +24,18 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; +import com.keylesspalace.tusky.R; + import java.util.ArrayList; import java.util.List; -class ReportAdapter extends RecyclerView.Adapter { - static class ReportStatus { +public class ReportAdapter extends RecyclerView.Adapter { + public static class ReportStatus { String id; Spanned content; boolean checked; - ReportStatus(String id, Spanned content, boolean checked) { + public ReportStatus(String id, Spanned content, boolean checked) { this.id = id; this.content = content; this.checked = checked; @@ -58,7 +60,7 @@ class ReportAdapter extends RecyclerView.Adapter { private List statusList; - ReportAdapter() { + public ReportAdapter() { super(); statusList = new ArrayList<>(); } @@ -82,13 +84,13 @@ class ReportAdapter extends RecyclerView.Adapter { return statusList.size(); } - void addItem(ReportStatus status) { + public void addItem(ReportStatus status) { int end = statusList.size(); statusList.add(status); notifyItemInserted(end); } - void addItems(List newStatuses) { + public void addItems(List newStatuses) { int end = statusList.size(); int added = 0; for (ReportStatus status : newStatuses) { @@ -102,7 +104,7 @@ class ReportAdapter extends RecyclerView.Adapter { } } - String[] getCheckedStatusIds() { + public String[] getCheckedStatusIds() { List idList = new ArrayList<>(); for (ReportStatus status : statusList) { if (status.checked) { diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java index 6a2e431df..1459dd4e0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java @@ -13,9 +13,10 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.content.Context; +import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.text.Spanned; @@ -26,7 +27,13 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.ToggleButton; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.util.RoundedTransformation; +import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.util.DateUtils; +import com.keylesspalace.tusky.util.LinkHelper; +import com.keylesspalace.tusky.util.ThemeUtils; import com.squareup.picasso.Picasso; import com.varunest.sparkbutton.SparkButton; import com.varunest.sparkbutton.SparkEventListener; @@ -100,7 +107,10 @@ class StatusViewHolder extends RecyclerView.ViewHolder { StatusActionListener listener) { /* Redirect URLSpan's in the status content to the listener for viewing tag pages and * account pages. */ - LinkHelper.setClickableText(this.content, content, mentions, listener); + Context context = this.content.getContext(); + boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean("useCustomTabs", true); + LinkHelper.setClickableText(this.content, content, mentions, useCustomTabs, listener); } private void setAvatar(String url) { diff --git a/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java similarity index 88% rename from app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java index 97dfc941c..46523f527 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java @@ -13,24 +13,27 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.interfaces.AdapterItemRemover; +import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.entity.Status; import java.util.ArrayList; import java.util.List; -class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { +public class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { private List statuses; private StatusActionListener statusActionListener; private int statusIndex; - ThreadAdapter(StatusActionListener listener) { + public ThreadAdapter(StatusActionListener listener) { this.statusActionListener = listener; this.statuses = new ArrayList<>(); this.statusIndex = 0; @@ -55,7 +58,7 @@ class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { return statuses.size(); } - Status getItem(int position) { + public Status getItem(int position) { return statuses.get(position); } @@ -76,7 +79,7 @@ class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { } } - int setStatus(Status status) { + public int setStatus(Status status) { if (statuses.size() > 0 && statuses.get(statusIndex).equals(status)) { // Do not add this status on refresh, it's already in there. statuses.set(statusIndex, status); @@ -88,7 +91,7 @@ class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { return i; } - void setContext(List ancestors, List descendants) { + public void setContext(List ancestors, List descendants) { Status mainStatus = null; // In case of refresh, remove old ancestors and descendants first. We'll remove all blindly, diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java similarity index 88% rename from app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java index 759a23579..892197b77 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TimelineAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.adapter; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; @@ -21,16 +21,19 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.interfaces.AdapterItemRemover; +import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.entity.Status; import java.util.ArrayList; import java.util.List; -class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover { +public class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover { private static final int VIEW_TYPE_STATUS = 0; private static final int VIEW_TYPE_FOOTER = 1; - enum FooterState { + public enum FooterState { EMPTY, END, LOADING @@ -40,7 +43,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover private StatusActionListener statusListener; private FooterState footerState = FooterState.END; - TimelineAdapter(StatusActionListener statusListener) { + public TimelineAdapter(StatusActionListener statusListener) { super(); statuses = new ArrayList<>(); this.statusListener = statusListener; @@ -79,7 +82,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover } } - void setFooterState(FooterState newFooterState) { + public void setFooterState(FooterState newFooterState) { FooterState oldValue = footerState; footerState = newFooterState; if (footerState != oldValue) { @@ -110,7 +113,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover } } - void update(List newStatuses) { + public void update(List newStatuses) { if (newStatuses == null || newStatuses.isEmpty()) { return; } @@ -131,7 +134,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover notifyDataSetChanged(); } - void addItems(List newStatuses) { + public void addItems(List newStatuses) { int end = statuses.size(); statuses.addAll(newStatuses); notifyItemRangeInserted(end, newStatuses.size()); @@ -142,7 +145,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover notifyItemRemoved(position); } - void removeAllByAccountId(String accountId) { + public void removeAllByAccountId(String accountId) { for (int i = 0; i < statuses.size();) { Status status = statuses.get(i); if (accountId.equals(status.account.id)) { @@ -155,7 +158,7 @@ class TimelineAdapter extends RecyclerView.Adapter implements AdapterItemRemover } @Nullable - Status getItem(int position) { + public Status getItem(int position) { if (position >= 0 && position < statuses.size()) { return statuses.get(position); } 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 e7be8e810..c802bd4aa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Account.java +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Account.java @@ -20,8 +20,8 @@ import android.text.Spanned; import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion; import com.google.gson.annotations.SerializedName; -import com.keylesspalace.tusky.HtmlUtils; -import com.keylesspalace.tusky.StringWithEmoji; +import com.keylesspalace.tusky.util.HtmlUtils; +import com.keylesspalace.tusky.json.StringWithEmoji; public class Account implements SearchSuggestion { public String id; diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountListFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.java similarity index 95% rename from app/src/main/java/com/keylesspalace/tusky/AccountListFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.java index 1128602f8..df2f57034 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountListFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.Intent; @@ -29,8 +29,22 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import com.keylesspalace.tusky.AccountActivity; +import com.keylesspalace.tusky.adapter.AccountAdapter; +import com.keylesspalace.tusky.adapter.BlocksAdapter; +import com.keylesspalace.tusky.adapter.FollowAdapter; +import com.keylesspalace.tusky.adapter.FollowRequestsAdapter; +import com.keylesspalace.tusky.adapter.MutesAdapter; +import com.keylesspalace.tusky.BaseActivity; import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Relationship; +import com.keylesspalace.tusky.interfaces.AccountActionListener; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.util.EndlessOnScrollListener; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import java.util.List; diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/BaseFragment.java similarity index 95% rename from app/src/main/java/com/keylesspalace/tusky/BaseFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/BaseFragment.java index 0a9dcbcf3..c0305d125 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/BaseFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.SharedPreferences; @@ -21,6 +21,8 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import com.keylesspalace.tusky.R; + import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeOptionsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ComposeOptionsFragment.java similarity index 97% rename from app/src/main/java/com/keylesspalace/tusky/ComposeOptionsFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/ComposeOptionsFragment.java index 1b24c27ee..ab5226020 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeOptionsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ComposeOptionsFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.res.ColorStateList; @@ -33,8 +33,11 @@ import android.widget.CompoundButton; import android.widget.RadioButton; import android.widget.RadioGroup; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.util.ThemeUtils; + public class ComposeOptionsFragment extends BottomSheetDialogFragment { - interface Listener { + public interface Listener { void onVisibilityChanged(String visibility); void onContentWarningChanged(boolean hideText); } diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 56afc0fd8..bef04290d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.SharedPreferences; @@ -31,8 +31,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import com.keylesspalace.tusky.MainActivity; +import com.keylesspalace.tusky.adapter.NotificationsAdapter; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Notification; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.StatusActionListener; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; +import com.keylesspalace.tusky.util.EndlessOnScrollListener; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import java.util.List; diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java similarity index 92% rename from app/src/main/java/com/keylesspalace/tusky/PreferencesFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java index 8820f5eb0..280098a76 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java @@ -13,11 +13,13 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.os.Bundle; import android.preference.PreferenceFragment; +import com.keylesspalace.tusky.R; + public class PreferencesFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/keylesspalace/tusky/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java similarity index 95% rename from app/src/main/java/com/keylesspalace/tusky/SFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java index 3af5cce0c..71b044261 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Intent; import android.content.SharedPreferences; @@ -27,8 +27,19 @@ import android.text.Spanned; import android.view.MenuItem; import android.view.View; +import com.keylesspalace.tusky.AccountActivity; +import com.keylesspalace.tusky.BaseActivity; +import com.keylesspalace.tusky.ComposeActivity; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.ReportActivity; +import com.keylesspalace.tusky.ViewTagActivity; +import com.keylesspalace.tusky.ViewThreadActivity; +import com.keylesspalace.tusky.ViewVideoActivity; import com.keylesspalace.tusky.entity.Relationship; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.AdapterItemRemover; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.util.HtmlUtils; import java.util.ArrayList; import java.util.List; @@ -45,7 +56,7 @@ import retrofit2.Response; * overlap functionality. So, I'm momentarily leaving it and hopefully working on those will clear * up what needs to be where. */ public abstract class SFragment extends BaseFragment { - interface OnUserRemovedListener { + public interface OnUserRemovedListener { void onUserRemoved(String accountId); } diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index d23bbed3a..4353c43fe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.SharedPreferences; @@ -31,7 +31,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.keylesspalace.tusky.MainActivity; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.adapter.TimelineAdapter; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.StatusActionListener; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; +import com.keylesspalace.tusky.util.EndlessOnScrollListener; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import java.util.List; @@ -47,7 +55,7 @@ public class TimelineFragment extends SFragment implements private Call> listCall; - enum Kind { + public enum Kind { HOME, PUBLIC_LOCAL, PUBLIC_FEDERATED, diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java similarity index 99% rename from app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java index fed8cbebd..9a5dbee23 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.app.AlertDialog; import android.app.DownloadManager; @@ -36,6 +36,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import com.keylesspalace.tusky.R; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java similarity index 93% rename from app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java rename to app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index ef1fc0a74..d02f43596 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.fragment; import android.content.Context; import android.graphics.drawable.Drawable; @@ -29,8 +29,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + +import com.keylesspalace.tusky.adapter.ThreadAdapter; +import com.keylesspalace.tusky.BaseActivity; import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.StatusContext; +import com.keylesspalace.tusky.network.MastodonAPI; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.interfaces.StatusActionListener; +import com.keylesspalace.tusky.interfaces.StatusRemoveListener; +import com.keylesspalace.tusky.util.ConversationLineItemDecoration; +import com.keylesspalace.tusky.util.Log; +import com.keylesspalace.tusky.util.ThemeUtils; import retrofit2.Call; import retrofit2.Callback; diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/AccountActionListener.java similarity index 92% rename from app/src/main/java/com/keylesspalace/tusky/AccountActionListener.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/AccountActionListener.java index bca609cdf..116bcae8f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActionListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/AccountActionListener.java @@ -13,9 +13,9 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.interfaces; -interface AccountActionListener { +public interface AccountActionListener { void onViewAccount(String id); void onMute(final boolean mute, final String id, final int position); void onBlock(final boolean block, final String id, final int position); diff --git a/app/src/main/java/com/keylesspalace/tusky/AdapterItemRemover.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/AdapterItemRemover.java similarity index 89% rename from app/src/main/java/com/keylesspalace/tusky/AdapterItemRemover.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/AdapterItemRemover.java index 634935ffa..5b49cbfa7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AdapterItemRemover.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/AdapterItemRemover.java @@ -13,8 +13,8 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.interfaces; -interface AdapterItemRemover { +public interface AdapterItemRemover { void removeItem(int position); } diff --git a/app/src/main/java/com/keylesspalace/tusky/LinkListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/LinkListener.java similarity index 90% rename from app/src/main/java/com/keylesspalace/tusky/LinkListener.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/LinkListener.java index 9e188ae42..62360e34a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LinkListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/LinkListener.java @@ -13,9 +13,9 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.interfaces; -interface LinkListener { +public interface LinkListener { void onViewTag(String tag); void onViewAccount(String id); } diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusActionListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java similarity index 91% rename from app/src/main/java/com/keylesspalace/tusky/StatusActionListener.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java index 1004bb824..b6b80f723 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusActionListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusActionListener.java @@ -13,13 +13,13 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.interfaces; import android.view.View; import com.keylesspalace.tusky.entity.Status; -interface StatusActionListener extends LinkListener { +public interface StatusActionListener extends LinkListener { void onReply(int position); void onReblog(final boolean reblog, final int position); void onFavourite(final boolean favourite, final int position); diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusRemoveListener.java similarity index 89% rename from app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java rename to app/src/main/java/com/keylesspalace/tusky/interfaces/StatusRemoveListener.java index 23111c6ab..c05f76573 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusRemoveListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/interfaces/StatusRemoveListener.java @@ -13,8 +13,8 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.interfaces; -interface StatusRemoveListener { +public interface StatusRemoveListener { void removePostsByUser(String accountId); } diff --git a/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java similarity index 93% rename from app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java index 3b47acc3b..305adba41 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SpannedTypeAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.json; import android.text.Spanned; @@ -22,6 +22,7 @@ import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; +import com.keylesspalace.tusky.util.HtmlUtils; import java.lang.reflect.Type; diff --git a/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java b/app/src/main/java/com/keylesspalace/tusky/json/StringWithEmoji.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java rename to app/src/main/java/com/keylesspalace/tusky/json/StringWithEmoji.java index cad8e8c08..d229c5a99 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StringWithEmoji.java +++ b/app/src/main/java/com/keylesspalace/tusky/json/StringWithEmoji.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.json; /** * This is just a wrapper class for a String. diff --git a/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/json/StringWithEmojiTypeAdapter.java similarity index 91% rename from app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/json/StringWithEmojiTypeAdapter.java index ecf18bcd9..86bd2c61b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StringWithEmojiTypeAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/json/StringWithEmojiTypeAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.json; import com.emojione.Emojione; import com.google.gson.JsonDeserializationContext; @@ -24,7 +24,7 @@ import com.google.gson.JsonParseException; import java.lang.reflect.Type; /** This is a type-based workaround to allow for shortcode conversion when loading display names. */ -class StringWithEmojiTypeAdapter implements JsonDeserializer { +public class StringWithEmojiTypeAdapter implements JsonDeserializer { @Override public StringWithEmoji deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { diff --git a/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java b/app/src/main/java/com/keylesspalace/tusky/network/MastodonAPI.java similarity index 99% rename from app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java rename to app/src/main/java/com/keylesspalace/tusky/network/MastodonAPI.java index 011fbc40a..aad04523c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MastodonAPI.java +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonAPI.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.network; import com.keylesspalace.tusky.entity.AccessToken; import com.keylesspalace.tusky.entity.Account; diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java b/app/src/main/java/com/keylesspalace/tusky/network/TuskyAPI.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java rename to app/src/main/java/com/keylesspalace/tusky/network/TuskyAPI.java index 4831949bd..700de83c8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java +++ b/app/src/main/java/com/keylesspalace/tusky/network/TuskyAPI.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.network; import okhttp3.ResponseBody; import retrofit2.Call; diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountPagerAdapter.java b/app/src/main/java/com/keylesspalace/tusky/pager/AccountPagerAdapter.java similarity index 84% rename from app/src/main/java/com/keylesspalace/tusky/AccountPagerAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/pager/AccountPagerAdapter.java index 505d89bd2..8f04ae186 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountPagerAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/pager/AccountPagerAdapter.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.pager; import android.content.Context; import android.support.v4.app.Fragment; @@ -24,23 +24,27 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.fragment.AccountListFragment; +import com.keylesspalace.tusky.fragment.TimelineFragment; + import java.util.ArrayList; import java.util.List; -class AccountPagerAdapter extends FragmentPagerAdapter { +public class AccountPagerAdapter extends FragmentPagerAdapter { private Context context; private String accountId; private String[] pageTitles; private List registeredFragments; - AccountPagerAdapter(FragmentManager manager, Context context, String accountId) { + public AccountPagerAdapter(FragmentManager manager, Context context, String accountId) { super(manager); this.context = context; this.accountId = accountId; registeredFragments = new ArrayList<>(); } - void setPageTitles(String[] titles) { + public void setPageTitles(String[] titles) { pageTitles = titles; } @@ -72,7 +76,7 @@ class AccountPagerAdapter extends FragmentPagerAdapter { return pageTitles[position]; } - View getTabView(int position, ViewGroup root) { + public View getTabView(int position, ViewGroup root) { View view = LayoutInflater.from(context).inflate(R.layout.tab_account, root, false); TextView title = (TextView) view.findViewById(R.id.title); title.setText(pageTitles[position]); @@ -92,7 +96,7 @@ class AccountPagerAdapter extends FragmentPagerAdapter { super.destroyItem(container, position, object); } - List getRegisteredFragments() { + public List getRegisteredFragments() { return registeredFragments; } } diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java b/app/src/main/java/com/keylesspalace/tusky/pager/TimelinePagerAdapter.java similarity index 87% rename from app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java rename to app/src/main/java/com/keylesspalace/tusky/pager/TimelinePagerAdapter.java index 03dc88dc9..826860413 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/pager/TimelinePagerAdapter.java @@ -13,31 +13,34 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.pager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.view.ViewGroup; +import com.keylesspalace.tusky.fragment.NotificationsFragment; +import com.keylesspalace.tusky.fragment.TimelineFragment; + import java.util.ArrayList; import java.util.List; -class TimelinePagerAdapter extends FragmentPagerAdapter { +public class TimelinePagerAdapter extends FragmentPagerAdapter { private int currentFragmentIndex; private List registeredFragments; - TimelinePagerAdapter(FragmentManager manager) { + public TimelinePagerAdapter(FragmentManager manager) { super(manager); currentFragmentIndex = 0; registeredFragments = new ArrayList<>(); } - Fragment getCurrentFragment() { + public Fragment getCurrentFragment() { return registeredFragments.get(currentFragmentIndex); } - List getRegisteredFragments() { + public List getRegisteredFragments() { return registeredFragments; } diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyTileService.java b/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.java similarity index 94% rename from app/src/main/java/com/keylesspalace/tusky/TuskyTileService.java rename to app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.java index 720e99664..677899286 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyTileService.java +++ b/app/src/main/java/com/keylesspalace/tusky/service/TuskyTileService.java @@ -13,12 +13,14 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.service; import android.annotation.TargetApi; import android.content.Intent; import android.service.quicksettings.TileService; +import com.keylesspalace.tusky.ComposeActivity; + /** * Small Addition that adds in a QuickSettings tile that opens the Compose activity when clicked * Created by ztepps on 4/3/17. diff --git a/app/src/main/java/com/keylesspalace/tusky/util/Assert.java b/app/src/main/java/com/keylesspalace/tusky/util/Assert.java new file mode 100644 index 000000000..c976184e5 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/Assert.java @@ -0,0 +1,29 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import com.keylesspalace.tusky.BuildConfig; + +/** Android Studio complains about built-in assertions so this is an alternative. */ +public class Assert { + private static boolean ENABLED = BuildConfig.DEBUG; + + public static void expect(boolean expression) { + if (ENABLED && !expression) { + throw new AssertionError(); + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java b/app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java similarity index 89% rename from app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java rename to app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java index e5fb8562d..dab773c35 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ConversationLineItemDecoration.java @@ -13,19 +13,17 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; -import android.util.TypedValue; import android.view.View; -import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import com.keylesspalace.tusky.R; -class ConversationLineItemDecoration extends RecyclerView.ItemDecoration { +public class ConversationLineItemDecoration extends RecyclerView.ItemDecoration { private final Context mContext; private final Drawable mDivider; diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CountUpDownLatch.java b/app/src/main/java/com/keylesspalace/tusky/util/CountUpDownLatch.java new file mode 100644 index 000000000..5791cf606 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/CountUpDownLatch.java @@ -0,0 +1,40 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +public class CountUpDownLatch { + private int count; + + public CountUpDownLatch() { + this.count = 0; + } + + public synchronized void countDown() { + count--; + notifyAll(); + } + + public synchronized void countUp() { + count++; + notifyAll(); + } + + public synchronized void await() throws InterruptedException { + while (count != 0) { + wait(); + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/CustomTabURLSpan.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java similarity index 96% rename from app/src/main/java/com/keylesspalace/tusky/CustomTabURLSpan.java rename to app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java index 7af9a84fe..bef07fcf1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/CustomTabURLSpan.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomTabURLSpan.java @@ -1,4 +1,4 @@ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.ActivityNotFoundException; import android.content.Context; @@ -11,6 +11,8 @@ import android.support.v4.content.ContextCompat; import android.text.style.URLSpan; import android.view.View; +import com.keylesspalace.tusky.R; + class CustomTabURLSpan extends URLSpan { CustomTabURLSpan(String url) { super(url); diff --git a/app/src/main/java/com/keylesspalace/tusky/CustomTabsHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomTabsHelper.java similarity index 99% rename from app/src/main/java/com/keylesspalace/tusky/CustomTabsHelper.java rename to app/src/main/java/com/keylesspalace/tusky/util/CustomTabsHelper.java index 456708638..f5add0475 100644 --- a/app/src/main/java/com/keylesspalace/tusky/CustomTabsHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomTabsHelper.java @@ -1,4 +1,4 @@ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; diff --git a/app/src/main/java/com/keylesspalace/tusky/util/DateUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/DateUtils.java new file mode 100644 index 000000000..b2cc49a2c --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/DateUtils.java @@ -0,0 +1,50 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +public class DateUtils { + /* This is a rough duplicate of android.text.format.DateUtils.getRelativeTimeSpanString, + * but even with the FORMAT_ABBREV_RELATIVE flag it wasn't abbreviating enough. */ + public static String getRelativeTimeSpanString(long then, long now) { + final long MINUTE = 60; + final long HOUR = 60 * MINUTE; + final long DAY = 24 * HOUR; + final long YEAR = 365 * DAY; + long span = (now - then) / 1000; + String prefix = ""; + if (span < 0) { + prefix = "in "; + span = -span; + } + String unit; + if (span < MINUTE) { + unit = "s"; + } else if (span < HOUR) { + span /= MINUTE; + unit = "m"; + } else if (span < DAY) { + span /= HOUR; + unit = "h"; + } else if (span < YEAR) { + span /= DAY; + unit = "d"; + } else { + span /= YEAR; + unit = "y"; + } + return prefix + span + unit; + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/DownsizeImageTask.java b/app/src/main/java/com/keylesspalace/tusky/util/DownsizeImageTask.java similarity index 97% rename from app/src/main/java/com/keylesspalace/tusky/DownsizeImageTask.java rename to app/src/main/java/com/keylesspalace/tusky/util/DownsizeImageTask.java index 56d50ed99..0cbc35a83 100644 --- a/app/src/main/java/com/keylesspalace/tusky/DownsizeImageTask.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/DownsizeImageTask.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.ContentResolver; import android.graphics.Bitmap; @@ -31,13 +31,13 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; -class DownsizeImageTask extends AsyncTask { +public class DownsizeImageTask extends AsyncTask { private int sizeLimit; private ContentResolver contentResolver; private Listener listener; private List resultList; - DownsizeImageTask(int sizeLimit, ContentResolver contentResolver, Listener listener) { + public DownsizeImageTask(int sizeLimit, ContentResolver contentResolver, Listener listener) { this.sizeLimit = sizeLimit; this.contentResolver = contentResolver; this.listener = listener; @@ -219,7 +219,7 @@ class DownsizeImageTask extends AsyncTask { super.onPostExecute(successful); } - interface Listener { + public interface Listener { void onSuccess(List contentList); void onFailure(); } diff --git a/app/src/main/java/com/keylesspalace/tusky/EditTextTyped.java b/app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java similarity index 98% rename from app/src/main/java/com/keylesspalace/tusky/EditTextTyped.java rename to app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java index 367180a2b..c32f666f4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditTextTyped.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/EditTextTyped.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.Context; import android.support.v13.view.inputmethod.EditorInfoCompat; diff --git a/app/src/main/java/com/keylesspalace/tusky/EndlessOnScrollListener.java b/app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java similarity index 90% rename from app/src/main/java/com/keylesspalace/tusky/EndlessOnScrollListener.java rename to app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java index 9c91a89a1..0b149de05 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EndlessOnScrollListener.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/EndlessOnScrollListener.java @@ -13,12 +13,12 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -abstract class EndlessOnScrollListener extends RecyclerView.OnScrollListener { +public abstract class EndlessOnScrollListener extends RecyclerView.OnScrollListener { private static final int VISIBLE_THRESHOLD = 15; private int currentPage; private int previousTotalItemCount; @@ -26,7 +26,7 @@ abstract class EndlessOnScrollListener extends RecyclerView.OnScrollListener { private int startingPageIndex; private LinearLayoutManager layoutManager; - EndlessOnScrollListener(LinearLayoutManager layoutManager) { + public EndlessOnScrollListener(LinearLayoutManager layoutManager) { this.layoutManager = layoutManager; currentPage = 0; previousTotalItemCount = 0; @@ -56,7 +56,7 @@ abstract class EndlessOnScrollListener extends RecyclerView.OnScrollListener { } } - void reset() { + public void reset() { currentPage = startingPageIndex; previousTotalItemCount = 0; loading = true; diff --git a/app/src/main/java/com/keylesspalace/tusky/FlowLayout.java b/app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java similarity index 98% rename from app/src/main/java/com/keylesspalace/tusky/FlowLayout.java rename to app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java index 44616643a..cf1f9e686 100644 --- a/app/src/main/java/com/keylesspalace/tusky/FlowLayout.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/FlowLayout.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.Context; import android.content.res.TypedArray; @@ -21,6 +21,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import com.keylesspalace.tusky.R; + public class FlowLayout extends ViewGroup { private int paddingHorizontal; // internal padding between child views private int paddingVertical; // diff --git a/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java new file mode 100644 index 000000000..eee9bb44d --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java @@ -0,0 +1,54 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import android.os.Build; +import android.text.Html; +import android.text.Spanned; + +public class HtmlUtils { + private static CharSequence trimTrailingWhitespace(CharSequence s) { + int i = s.length(); + do { + i--; + } while (i >= 0 && Character.isWhitespace(s.charAt(i))); + return s.subSequence(0, i + 1); + } + + @SuppressWarnings("deprecation") + public static Spanned fromHtml(String html) { + Spanned result; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); + } else { + result = Html.fromHtml(html); + } + /* Html.fromHtml returns trailing whitespace if the html ends in a

tag, which + * all status contents do, so it should be trimmed. */ + return (Spanned) trimTrailingWhitespace(result); + } + + @SuppressWarnings("deprecation") + public static String toHtml(Spanned text) { + String result; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + result = Html.toHtml(text, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE); + } else { + result = Html.toHtml(text); + } + return result; + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.java new file mode 100644 index 000000000..2b17eeeb1 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.java @@ -0,0 +1,44 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import android.support.annotation.Nullable; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class IOUtils { + public static void closeQuietly(@Nullable InputStream stream) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // intentionally unhandled + } + } + + public static void closeQuietly(@Nullable OutputStream stream) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // intentionally unhandled + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java similarity index 87% rename from app/src/main/java/com/keylesspalace/tusky/LinkHelper.java rename to app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java index ba9a591fe..46ddd5f17 100644 --- a/app/src/main/java/com/keylesspalace/tusky/LinkHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.java @@ -13,9 +13,8 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; -import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -26,14 +25,13 @@ import android.view.View; import android.widget.TextView; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.LinkListener; -class LinkHelper { - static void setClickableText(TextView view, Spanned content, - @Nullable Status.Mention[] mentions, - final LinkListener listener) { +public class LinkHelper { + public static void setClickableText(TextView view, Spanned content, + @Nullable Status.Mention[] mentions, boolean useCustomTabs, + final LinkListener listener) { SpannableStringBuilder builder = new SpannableStringBuilder(content); - boolean useCustomTabs = PreferenceManager.getDefaultSharedPreferences(view.getContext()) - .getBoolean("customTabs", true); URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class); for (URLSpan span : urlSpans) { int start = builder.getSpanStart(span); diff --git a/app/src/main/java/com/keylesspalace/tusky/util/Log.java b/app/src/main/java/com/keylesspalace/tusky/util/Log.java new file mode 100644 index 000000000..26d3d85fe --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/Log.java @@ -0,0 +1,53 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import com.keylesspalace.tusky.BuildConfig; + +/**A wrapper for android.util.Log that allows for disabling logging, such as for release builds.*/ +public class Log { + private static final boolean LOGGING_ENABLED = BuildConfig.DEBUG; + + public static void i(String tag, String string) { + if (LOGGING_ENABLED) { + android.util.Log.i(tag, string); + } + } + + public static void e(String tag, String string) { + if (LOGGING_ENABLED) { + android.util.Log.e(tag, string); + } + } + + public static void d(String tag, String string) { + if (LOGGING_ENABLED) { + android.util.Log.d(tag, string); + } + } + + public static void v(String tag, String string) { + if (LOGGING_ENABLED) { + android.util.Log.v(tag, string); + } + } + + public static void w(String tag, String string) { + if (LOGGING_ENABLED) { + android.util.Log.w(tag, string); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationClearBroadcastReceiver.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationClearBroadcastReceiver.java similarity index 97% rename from app/src/main/java/com/keylesspalace/tusky/NotificationClearBroadcastReceiver.java rename to app/src/main/java/com/keylesspalace/tusky/util/NotificationClearBroadcastReceiver.java index 9508dac6a..d18902e36 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationClearBroadcastReceiver.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationClearBroadcastReceiver.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.content.BroadcastReceiver; import android.content.Context; diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java similarity index 97% rename from app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java rename to app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java index 8c7bfb85d..e5bc70033 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationMaker.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationMaker.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.app.NotificationManager; import android.app.PendingIntent; @@ -29,6 +29,8 @@ import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.support.v4.app.TaskStackBuilder; +import com.keylesspalace.tusky.MainActivity; +import com.keylesspalace.tusky.R; import com.keylesspalace.tusky.entity.Notification; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; @@ -36,8 +38,8 @@ import com.squareup.picasso.Target; import org.json.JSONArray; import org.json.JSONException; -class NotificationMaker { - static void make(final Context context, final int notifyId, Notification body) { +public class NotificationMaker { + public static void make(final Context context, final int notifyId, Notification body) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences notificationPreferences = context.getSharedPreferences( diff --git a/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java new file mode 100644 index 000000000..c90002d20 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/OkHttpUtils.java @@ -0,0 +1,244 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is part of Tusky. + * + * Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with Tusky. If + * not, see . */ + +package com.keylesspalace.tusky.util; + +import android.os.Build; +import android.support.annotation.NonNull; + +import com.keylesspalace.tusky.BuildConfig; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import okhttp3.ConnectionSpec; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class OkHttpUtils { + static final String TAG = "OkHttpUtils"; // logging tag + + /** + * Makes a Builder with the maximum range of TLS versions and cipher suites enabled. + * + * It first tries the "approved" list of cipher suites given in OkHttp (the default in + * ConnectionSpec.MODERN_TLS) and if that doesn't work falls back to the set of ALL enabled, + * then falls back to plain http. + * + * API level 24 has a regression in elliptic curves where it only supports secp256r1, so this + * first tries a fallback without elliptic curves at all, and then tries them after. + * + * TLS 1.1 and 1.2 have to be manually enabled on API levels 16-20. + */ + @NonNull + public static OkHttpClient.Builder getCompatibleClientBuilder() { + ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .allEnabledCipherSuites() + .supportsTlsExtensions(true) + .build(); + + List specList = new ArrayList<>(); + specList.add(ConnectionSpec.MODERN_TLS); + addNougatFixConnectionSpec(specList); + specList.add(fallback); + specList.add(ConnectionSpec.CLEARTEXT); + + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .addInterceptor(getUserAgentInterceptor()) + .connectionSpecs(specList); + + return enableHigherTlsOnPreLollipop(builder); + } + + @NonNull + public static OkHttpClient getCompatibleClient() { + return getCompatibleClientBuilder().build(); + } + + /** + * Add a custom User-Agent that contains Tusky & Android Version to all requests + * Example: + * User-Agent: Tusky/1.1.2 Android/5.0.2 + */ + @NonNull + private static Interceptor getUserAgentInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + Request requestWithUserAgent = originalRequest.newBuilder() + .header("User-Agent", "Tusky/"+ BuildConfig.VERSION_NAME+" Android/"+Build.VERSION.RELEASE) + .build(); + return chain.proceed(requestWithUserAgent); + } + }; + } + + + /** + * Android version Nougat has a regression where elliptic curve cipher suites are supported, but + * only the curve secp256r1 is allowed. So, first it's best to just disable all elliptic + * ciphers, try the connection, and fall back to the all cipher suites enabled list after. + */ + private static void addNougatFixConnectionSpec(List specList) { + if (Build.VERSION.SDK_INT != Build.VERSION_CODES.N) { + return; + } + SSLSocketFactory socketFactory; + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); + } + + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { trustManager }, null); + socketFactory = sslContext.getSocketFactory(); + } catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e) { + Log.e(TAG, "Failed obtaining the SSL socket factory."); + return; + } + String[] cipherSuites = socketFactory.getDefaultCipherSuites(); + ArrayList allowedList = new ArrayList<>(); + for (String suite : cipherSuites) { + if (!suite.contains("ECDH")) { + allowedList.add(suite); + } + } + ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .cipherSuites(allowedList.toArray(new String[0])) + .supportsTlsExtensions(true) + .build(); + specList.add(spec); + } + + private static OkHttpClient.Builder enableHigherTlsOnPreLollipop(OkHttpClient.Builder builder) { + if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) { + try { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); + } + + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, new TrustManager[] { trustManager }, null); + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + builder.sslSocketFactory(new SSLSocketFactoryCompat(sslSocketFactory), + trustManager); + } catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e) { + Log.e(TAG, "Failed enabling TLS 1.1 & 1.2. " + e.getMessage()); + } + } + + return builder; + } + + private static class SSLSocketFactoryCompat extends SSLSocketFactory { + private static final String[] DESIRED_TLS_VERSIONS = { "TLSv1", "TLSv1.1", "TLSv1.2", + "TLSv1.3" }; + + final SSLSocketFactory delegate; + + SSLSocketFactoryCompat(SSLSocketFactory base) { + this.delegate = base; + } + + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + return patch(delegate.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException { + return patch(delegate.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return patch(delegate.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException { + return patch(delegate.createSocket(address, port, localAddress, localPort)); + } + + @NonNull + private static String[] getMatches(String[] wanted, String[] have) { + List a = new ArrayList<>(Arrays.asList(wanted)); + List b = Arrays.asList(have); + a.retainAll(b); + return a.toArray(new String[0]); + } + + private Socket patch(Socket socket) { + if (socket instanceof SSLSocket) { + SSLSocket sslSocket = (SSLSocket) socket; + String[] protocols = getMatches(DESIRED_TLS_VERSIONS, + sslSocket.getSupportedProtocols()); + sslSocket.setEnabledProtocols(protocols); + } + return socket; + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/RoundedTransformation.java b/app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java similarity index 98% rename from app/src/main/java/com/keylesspalace/tusky/RoundedTransformation.java rename to app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java index bd4192b51..ff153d687 100644 --- a/app/src/main/java/com/keylesspalace/tusky/RoundedTransformation.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/RoundedTransformation.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky; +package com.keylesspalace.tusky.util; import android.graphics.Bitmap; import android.graphics.BitmapShader; diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.java new file mode 100644 index 000000000..81a82d2b8 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.java @@ -0,0 +1,129 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import android.text.Spannable; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; + +public class SpanUtils { + private static class FindCharsResult { + int charIndex; + int stringIndex; + + FindCharsResult() { + charIndex = -1; + stringIndex = -1; + } + } + + private static FindCharsResult findChars(String string, int fromIndex, char[] chars) { + FindCharsResult result = new FindCharsResult(); + final int length = string.length(); + for (int i = fromIndex; i < length; i++) { + char c = string.charAt(i); + for (int j = 0; j < chars.length; j++) { + if (chars[j] == c) { + result.charIndex = j; + result.stringIndex = i; + return result; + } + } + } + return result; + } + + private static FindCharsResult findStart(String string, int fromIndex, char[] chars) { + final int length = string.length(); + while (fromIndex < length) { + FindCharsResult found = findChars(string, fromIndex, chars); + int i = found.stringIndex; + if (i < 0) { + break; + } else if (i == 0 || i >= 1 && Character.isWhitespace(string.codePointBefore(i))) { + return found; + } else { + fromIndex = i + 1; + } + } + return new FindCharsResult(); + } + + private static int findEndOfHashtag(String string, int fromIndex) { + final int length = string.length(); + for (int i = fromIndex + 1; i < length;) { + int codepoint = string.codePointAt(i); + if (Character.isWhitespace(codepoint)) { + return i; + } else if (codepoint == '#') { + return -1; + } + i += Character.charCount(codepoint); + } + return length; + } + + private static int findEndOfMention(String string, int fromIndex) { + int atCount = 0; + final int length = string.length(); + for (int i = fromIndex + 1; i < length;) { + int codepoint = string.codePointAt(i); + if (Character.isWhitespace(codepoint)) { + return i; + } else if (codepoint == '@') { + atCount += 1; + if (atCount >= 2) { + return -1; + } + } + i += Character.charCount(codepoint); + } + return length; + } + + public static void highlightSpans(Spannable text, int colour) { + // Strip all existing colour spans. + int n = text.length(); + ForegroundColorSpan[] oldSpans = text.getSpans(0, n, ForegroundColorSpan.class); + for (int i = oldSpans.length - 1; i >= 0; i--) { + text.removeSpan(oldSpans[i]); + } + // Colour the mentions and hashtags. + String string = text.toString(); + int start; + int end = 0; + while (end < n) { + char[] chars = { '#', '@' }; + FindCharsResult found = findStart(string, end, chars); + start = found.stringIndex; + if (start < 0) { + break; + } + if (found.charIndex == 0) { + end = findEndOfHashtag(string, start); + } else if (found.charIndex == 1) { + end = findEndOfMention(string, start); + } else { + break; + } + if (end < 0) { + break; + } + text.setSpan(new ForegroundColorSpan(colour), start, end, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java new file mode 100644 index 000000000..310341faf --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java @@ -0,0 +1,68 @@ +/* Copyright 2017 Andrew Dawson + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.util; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.support.annotation.AttrRes; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; +import android.util.TypedValue; +import android.widget.ImageView; + +public class ThemeUtils { + public static Drawable getDrawable(Context context, @AttrRes int attribute, + @DrawableRes int fallbackDrawable) { + TypedValue value = new TypedValue(); + @DrawableRes int resourceId; + if (context.getTheme().resolveAttribute(attribute, value, true)) { + resourceId = value.resourceId; + } else { + resourceId = fallbackDrawable; + } + return ContextCompat.getDrawable(context, resourceId); + } + + public static @DrawableRes int getDrawableId(Context context, @AttrRes int attribute, + @DrawableRes int fallbackDrawableId) { + TypedValue value = new TypedValue(); + if (context.getTheme().resolveAttribute(attribute, value, true)) { + return value.resourceId; + } else { + return fallbackDrawableId; + } + } + + public static @ColorInt int getColor(Context context, @AttrRes int attribute) { + TypedValue value = new TypedValue(); + if (context.getTheme().resolveAttribute(attribute, value, true)) { + return value.data; + } else { + return Color.BLACK; + } + } + + public static void setImageViewTint(ImageView view, @AttrRes int attribute) { + view.setColorFilter(getColor(view.getContext(), attribute), PorterDuff.Mode.SRC_IN); + } + + public static void setDrawableTint(Context context, Drawable drawable, @AttrRes int attribute) { + drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN); + } +} diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml index 9807e944b..0252cce7b 100644 --- a/app/src/main/res/layout/activity_compose.xml +++ b/app/src/main/res/layout/activity_compose.xml @@ -54,7 +54,7 @@ android:paddingLeft="16dp" android:paddingRight="16dp"> - - - +