diff --git a/app/build.gradle b/app/build.gradle
index 553f1fee..a71f3b4d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -25,6 +25,9 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
+ compile('com.mikepenz:materialdrawer:5.8.2@aar') {
+ transitive = true
+ }
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:support-v13:25.2.0'
@@ -32,16 +35,19 @@ dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.pkmmte.view:circularimageview:1.1'
compile 'com.github.peter9870:sparkbutton:master'
- testCompile 'junit:junit:4.12'
compile 'com.mikhaellopez:circularfillableloaders:1.2.0'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
- compile('com.mikepenz:materialdrawer:5.8.2@aar') {
- transitive = true
- }
compile 'com.github.chrisbanes:PhotoView:1.3.1'
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
compile 'com.github.arimorty:floatingsearchview:2.0.3'
compile 'com.jakewharton:butterknife:8.4.0'
+ compile 'com.google.firebase:firebase-messaging:10.0.1'
+ compile 'com.google.firebase:firebase-crash:10.0.1'
+ testCompile 'junit:junit:4.12'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}
+
+
+
+apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4bdc081e..84193826 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
-
+
+
+
-
+
+
-
+
@@ -42,10 +47,17 @@
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
index 14ec4af3..689df0dc 100644
--- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java
@@ -47,12 +47,14 @@ import retrofit2.converter.gson.GsonConverterFactory;
* activity extend from it. */
public class BaseActivity extends AppCompatActivity {
protected MastodonAPI mastodonAPI;
+ protected TuskyAPI tuskyAPI;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createMastodonAPI();
+ createTuskyAPI();
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("lightTheme", false)) {
setTheme(R.style.AppTheme_Light);
@@ -121,6 +123,14 @@ public class BaseActivity extends AppCompatActivity {
mastodonAPI = retrofit.create(MastodonAPI.class);
}
+ protected void createTuskyAPI() {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl(getString(R.string.tusky_api_url))
+ .build();
+
+ tuskyAPI = retrofit.create(TuskyAPI.class);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
TypedValue value = new TypedValue();
diff --git a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
index 52f64a52..6bd00803 100644
--- a/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/BlocksAdapter.java
@@ -20,15 +20,20 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.keylesspalace.tusky.entity.Account;
+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 BlocksAdapter extends AccountAdapter {
private static final int VIEW_TYPE_BLOCKED_USER = 0;
private static final int VIEW_TYPE_FOOTER = 1;
@@ -88,19 +93,17 @@ class BlocksAdapter extends AccountAdapter {
notifyItemChanged(position);
}
- private static class BlockedUserViewHolder extends RecyclerView.ViewHolder {
- private ImageView avatar;
- private TextView username;
- private TextView displayName;
- private Button unblock;
+ static class BlockedUserViewHolder extends RecyclerView.ViewHolder {
+ @BindView(R.id.blocked_user_avatar) CircularImageView avatar;
+ @BindView(R.id.blocked_user_username) TextView username;
+ @BindView(R.id.blocked_user_display_name) TextView displayName;
+ @BindView(R.id.blocked_user_unblock) ImageButton unblock;
+
private String id;
BlockedUserViewHolder(View itemView) {
super(itemView);
- avatar = (ImageView) itemView.findViewById(R.id.blocked_user_avatar);
- displayName = (TextView) itemView.findViewById(R.id.blocked_user_display_name);
- username = (TextView) itemView.findViewById(R.id.blocked_user_username);
- unblock = (Button) itemView.findViewById(R.id.blocked_user_unblock);
+ ButterKnife.bind(this, itemView);
}
void setupWithAccount(Account account) {
@@ -118,13 +121,6 @@ class BlocksAdapter extends AccountAdapter {
void setupActionListener(final AccountActionListener listener, final boolean blocked,
final int position) {
- int unblockTextId;
- if (blocked) {
- unblockTextId = R.string.action_unblock;
- } else {
- unblockTextId = R.string.action_block;
- }
- unblock.setText(unblock.getContext().getString(unblockTextId));
unblock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java b/app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java
new file mode 100644
index 00000000..9dbce756
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/ConversationLineItemDecoration.java
@@ -0,0 +1,42 @@
+package com.keylesspalace.tusky;
+
+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;
+
+class ConversationLineItemDecoration extends RecyclerView.ItemDecoration {
+ private final Context mContext;
+ private final Drawable mDivider;
+
+ public ConversationLineItemDecoration(Context context, Drawable divider) {
+ mContext = context;
+ mDivider = divider;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ // Fun fact: this method draws in pixels, but all layouts are in DP, so I'm using the divider's
+ // own 2dp width to calculate what I want
+ int dividerLeft = parent.getPaddingLeft() + mContext.getResources().getDimensionPixelSize(R.dimen.status_left_line_margin);
+ int dividerRight = dividerLeft + mDivider.getIntrinsicWidth();
+
+ int childCount = parent.getChildCount();
+ int avatarMargin = mContext.getResources().getDimensionPixelSize(R.dimen.account_avatar_margin);
+
+ for (int i = 0; i < childCount; i++) {
+ View child = parent.getChildAt(i);
+
+ int dividerTop = child.getTop() + (i == 0 ? avatarMargin : 0);
+ int dividerBottom = (i == childCount - 1 ? child.getTop() + avatarMargin : child.getBottom());
+
+ mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
+ mDivider.draw(c);
+ }
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java
index d15708cf..248fa6b6 100644
--- a/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/FollowAdapter.java
@@ -24,8 +24,11 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.keylesspalace.tusky.entity.Account;
+import com.pkmmte.view.CircularImageView;
import com.squareup.picasso.Picasso;
+import butterknife.ButterKnife;
+
/** Both for follows and following lists. */
class FollowAdapter extends AccountAdapter {
private static final int VIEW_TYPE_ACCOUNT = 0;
@@ -77,8 +80,7 @@ class FollowAdapter extends AccountAdapter {
private View container;
private TextView username;
private TextView displayName;
- private TextView note;
- private ImageView avatar;
+ private CircularImageView avatar;
private String id;
AccountViewHolder(View itemView) {
@@ -86,8 +88,7 @@ class FollowAdapter extends AccountAdapter {
container = itemView.findViewById(R.id.account_container);
username = (TextView) itemView.findViewById(R.id.account_username);
displayName = (TextView) itemView.findViewById(R.id.account_display_name);
- note = (TextView) itemView.findViewById(R.id.account_note);
- avatar = (ImageView) itemView.findViewById(R.id.account_avatar);
+ avatar = (CircularImageView) itemView.findViewById(R.id.account_avatar);
}
void setupWithAccount(Account account) {
@@ -96,7 +97,6 @@ class FollowAdapter extends AccountAdapter {
String formattedUsername = String.format(format, account.username);
username.setText(formattedUsername);
displayName.setText(account.getDisplayName());
- note.setText(account.note);
Context context = avatar.getContext();
Picasso.with(context)
.load(account.avatar)
diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
index 22626433..cb1504ef 100644
--- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java
@@ -20,6 +20,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -27,6 +28,7 @@ import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
@@ -41,6 +43,7 @@ import android.widget.TextView;
import com.arlib.floatingsearchview.FloatingSearchView;
import com.arlib.floatingsearchview.suggestions.SearchSuggestionsAdapter;
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion;
+import com.google.firebase.iid.FirebaseInstanceId;
import com.keylesspalace.tusky.entity.Account;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.materialdrawer.AccountHeader;
@@ -60,6 +63,9 @@ import com.squareup.picasso.Picasso;
import java.util.List;
import java.util.Stack;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@@ -67,28 +73,27 @@ import retrofit2.Response;
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity"; // logging tag and Volley request tag
- private AlarmManager alarmManager;
- private PendingIntent serviceAlarmIntent;
- private boolean notificationServiceEnabled;
private String loggedInAccountId;
private String loggedInAccountUsername;
Stack pageHistory = new Stack();
- private ViewPager viewPager;
private AccountHeader headerResult;
private Drawer drawer;
+ @BindView(R.id.floating_search_view) FloatingSearchView searchView;
+ @BindView(R.id.floating_btn) FloatingActionButton floatingBtn;
+ @BindView(R.id.tab_layout) TabLayout tabLayout;
+ @BindView(R.id.pager) ViewPager viewPager;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ ButterKnife.bind(this);
+
// Fetch user info while we're doing other things.
fetchUserInfo();
- //Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- //setSupportActionBar(toolbar);
-
- FloatingActionButton floatingBtn = (FloatingActionButton) findViewById(R.id.floating_btn);
floatingBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -97,8 +102,93 @@ public class MainActivity extends BaseActivity {
}
});
- final FloatingSearchView searchView = (FloatingSearchView) findViewById(R.id.floating_search_view);
+ setupDrawer();
+ setupSearchView();
+ // Setup the tabs and timeline pager.
+ TimelinePagerAdapter adapter = new TimelinePagerAdapter(getSupportFragmentManager());
+ String[] pageTitles = {
+ getString(R.string.title_home),
+ getString(R.string.title_notifications),
+ getString(R.string.title_public)
+ };
+ adapter.setPageTitles(pageTitles);
+
+ int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
+ viewPager.setPageMargin(pageMargin);
+ Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
+ R.drawable.tab_page_margin_dark);
+ viewPager.setPageMarginDrawable(pageMarginDrawable);
+ viewPager.setAdapter(adapter);
+
+ tabLayout.setupWithViewPager(viewPager);
+
+ tabLayout.getTabAt(0).setIcon(R.drawable.ic_home_24dp);
+ tabLayout.getTabAt(1).setIcon(R.drawable.ic_notifications_24dp);
+ tabLayout.getTabAt(2).setIcon(R.drawable.ic_public_24dp);
+
+ tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ viewPager.setCurrentItem(tab.getPosition());
+
+ if (pageHistory.empty()) {
+ pageHistory.push(0);
+ }
+
+ if (pageHistory.contains(tab.getPosition())) {
+ pageHistory.remove(pageHistory.indexOf(tab.getPosition()));
+ }
+
+ pageHistory.push(tab.getPosition());
+ tintTab(tab, true);
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ tintTab(tab, false);
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+
+ }
+ });
+
+ Intent intent = getIntent();
+
+ if (intent != null) {
+ int tabPosition = intent.getIntExtra("tab_position", 0);
+
+ if (tabPosition != 0) {
+ tabLayout.getTabAt(tabPosition).select();
+ tintTab(tabLayout.getTabAt(tabPosition), true);
+ } else {
+ tintTab(tabLayout.getTabAt(0), true);
+ }
+ } else {
+ tintTab(tabLayout.getTabAt(0), true);
+ }
+
+ // Setup push notifications
+ tuskyAPI.register(getBaseUrl(), getAccessToken(), FirebaseInstanceId.getInstance().getToken()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ }
+
+ private void tintTab(TabLayout.Tab tab, boolean tinted) {
+ tab.getIcon().setColorFilter(ContextCompat.getColor(this, tinted ? R.color.color_accent_dark : R.color.toolbar_icon_dark), PorterDuff.Mode.SRC_IN);
+ }
+
+ private void setupDrawer() {
headerResult = new AccountHeaderBuilder()
.withActivity(this)
.withSelectionListEnabledForSingleProfile(false)
@@ -152,18 +242,7 @@ public class MainActivity extends BaseActivity {
Intent intent = new Intent(MainActivity.this, PreferencesActivity.class);
startActivity(intent);
} else if (drawerItemIdentifier == 4) {
- if (notificationServiceEnabled) {
- alarmManager.cancel(serviceAlarmIntent);
- }
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = preferences.edit();
- editor.remove("domain");
- editor.remove("accessToken");
- editor.apply();
- Intent intent = new Intent(MainActivity.this, SplashActivity.class);
- startActivity(intent);
- finish();
+ logout();
}
}
@@ -171,7 +250,33 @@ public class MainActivity extends BaseActivity {
}
})
.build();
+ }
+ private void logout() {
+ tuskyAPI.unregister(getBaseUrl(), getAccessToken()).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+
+ SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove("domain");
+ editor.remove("accessToken");
+ editor.apply();
+
+ Intent intent = new Intent(MainActivity.this, LoginActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void setupSearchView() {
searchView.attachNavigationDrawerToMenuButton(drawer.getDrawerLayout());
searchView.setOnQueryChangeListener(new FloatingSearchView.OnQueryChangeListener() {
@@ -237,70 +342,6 @@ public class MainActivity extends BaseActivity {
textView.setEllipsize(TextUtils.TruncateAt.END);
}
});
-
- // Setup the tabs and timeline pager.
- TimelinePagerAdapter adapter = new TimelinePagerAdapter(getSupportFragmentManager());
- String[] pageTitles = {
- getString(R.string.title_home),
- getString(R.string.title_notifications),
- getString(R.string.title_public)
- };
- adapter.setPageTitles(pageTitles);
- viewPager = (ViewPager) findViewById(R.id.pager);
- int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
- viewPager.setPageMargin(pageMargin);
- Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
- R.drawable.tab_page_margin_dark);
- viewPager.setPageMarginDrawable(pageMarginDrawable);
- viewPager.setAdapter(adapter);
-
- TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
- tabLayout.setupWithViewPager(viewPager);
-
- tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
- @Override
- public void onTabSelected(TabLayout.Tab tab) {
- viewPager.setCurrentItem(tab.getPosition());
-
- if (pageHistory.empty()) {
- pageHistory.push(0);
- }
-
- if (pageHistory.contains(tab.getPosition())) {
- pageHistory.remove(pageHistory.indexOf(tab.getPosition()));
- }
-
- pageHistory.push(tab.getPosition());
- }
-
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {
-
- }
-
- @Override
- public void onTabReselected(TabLayout.Tab tab) {
-
- }
- });
-
- // Retrieve notification update preference.
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- notificationServiceEnabled = preferences.getBoolean("pullNotifications", true);
- String minutesString = preferences.getString("pullNotificationCheckInterval", "15");
- long notificationCheckInterval = 60 * 1000 * Integer.valueOf(minutesString);
- // Start up the PullNotificationsService.
- alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(this, PullNotificationService.class);
- final int SERVICE_REQUEST_CODE = 8574603; // This number is arbitrary.
- serviceAlarmIntent = PendingIntent.getService(this, SERVICE_REQUEST_CODE, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- if (notificationServiceEnabled) {
- alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime(), notificationCheckInterval, serviceAlarmIntent);
- } else {
- alarmManager.cancel(serviceAlarmIntent);
- }
}
private void fetchUserInfo() {
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
new file mode 100644
index 00000000..7c173369
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseInstanceIdService.java
@@ -0,0 +1,61 @@
+package com.keylesspalace.tusky;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
+
+ private TuskyAPI tuskyAPI;
+
+ protected void createTuskyAPI() {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl(getString(R.string.tusky_api_url))
+ .build();
+
+ tuskyAPI = retrofit.create(TuskyAPI.class);
+ }
+
+ @Override
+ public void onTokenRefresh() {
+ createTuskyAPI();
+
+ String refreshedToken = FirebaseInstanceId.getInstance().getToken();
+ SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ String accessToken = preferences.getString("accessToken", null);
+ String domain = preferences.getString("domain", null);
+
+ if (accessToken != null && domain != null) {
+ tuskyAPI.unregister("https://" + domain, accessToken).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ tuskyAPI.register("https://" + domain, accessToken, refreshedToken).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
new file mode 100644
index 00000000..31060869
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/MyFirebaseMessagingService.java
@@ -0,0 +1,191 @@
+package com.keylesspalace.tusky;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
+import android.text.Spanned;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.keylesspalace.tusky.entity.Notification;
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class MyFirebaseMessagingService extends FirebaseMessagingService {
+ private MastodonAPI mastodonAPI;
+ private static final String TAG = "MyFirebaseMessagingService";
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ Log.d(TAG, remoteMessage.getFrom());
+ Log.d(TAG, remoteMessage.toString());
+
+ String notificationId = remoteMessage.getData().get("notification_id");
+
+ if (notificationId == null) {
+ Log.e(TAG, "No notification ID in payload!!");
+ return;
+ }
+
+ Log.d(TAG, notificationId);
+
+ createMastodonAPI();
+
+ mastodonAPI.notification(notificationId).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+ buildNotification(response.body());
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+
+ }
+ });
+ }
+
+ private void createMastodonAPI() {
+ SharedPreferences preferences = getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ final String domain = preferences.getString("domain", null);
+ final String accessToken = preferences.getString("accessToken", null);
+
+ OkHttpClient okHttpClient = new OkHttpClient.Builder()
+ .addInterceptor(new Interceptor() {
+ @Override
+ public okhttp3.Response intercept(Chain chain) throws IOException {
+ Request originalRequest = chain.request();
+
+ Request.Builder builder = originalRequest.newBuilder()
+ .header("Authorization", String.format("Bearer %s", accessToken));
+
+ Request newRequest = builder.build();
+
+ return chain.proceed(newRequest);
+ }
+ })
+ .build();
+
+ Gson gson = new GsonBuilder()
+ .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
+ .create();
+
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl("https://" + domain)
+ .client(okHttpClient)
+ .addConverterFactory(GsonConverterFactory.create(gson))
+ .build();
+
+ mastodonAPI = retrofit.create(MastodonAPI.class);
+ }
+
+ private String truncateWithEllipses(String string, int limit) {
+ if (string.length() < limit) {
+ return string;
+ } else {
+ return string.substring(0, limit - 3) + "...";
+ }
+ }
+
+ private void buildNotification(Notification body) {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ Intent resultIntent = new Intent(this, MainActivity.class);
+ resultIntent.putExtra("tab_position", 1);
+ TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+ stackBuilder.addParentStack(MainActivity.class);
+ stackBuilder.addNextIntent(resultIntent);
+ PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_notify)
+ .setAutoCancel(true)
+ .setContentIntent(resultPendingIntent)
+ .setDefaults(0); // So it doesn't ring twice, notify only in Target callback
+
+ final Integer mId = (int)(System.currentTimeMillis() / 1000);
+
+ Target mTarget = new Target() {
+ @Override
+ public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+ builder.setLargeIcon(bitmap);
+
+ if (preferences.getBoolean("notificationAlertSound", true)) {
+ builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
+ }
+
+ if (preferences.getBoolean("notificationStyleVibrate", false)) {
+ builder.setVibrate(new long[] { 500, 500 });
+ }
+
+ if (preferences.getBoolean("notificationStyleLight", false)) {
+ builder.setLights(0xFF00FF8F, 300, 1000);
+ }
+
+ ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build());
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+
+ }
+ };
+
+ Picasso.with(this)
+ .load(body.account.avatar)
+ .placeholder(R.drawable.avatar_default)
+ .into(mTarget);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
+ builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
+ }
+
+ switch (body.type) {
+ case MENTION:
+ builder.setContentTitle(String.format(getString(R.string.notification_mention_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ case FOLLOW:
+ builder.setContentTitle(String.format(getString(R.string.notification_follow_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.account.username, 40));
+ break;
+ case FAVOURITE:
+ builder.setContentTitle(String.format(getString(R.string.notification_favourite_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ case REBLOG:
+ builder.setContentTitle(String.format(getString(R.string.notification_reblog_format), body.account.getDisplayName()))
+ .setContentText(truncateWithEllipses(body.status.content.toString(), 40));
+ break;
+ }
+
+ ((NotificationManager) (getSystemService(NOTIFICATION_SERVICE))).notify(mId, builder.build());
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
index 7d3f84ed..60798f95 100644
--- a/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/NotificationsFragment.java
@@ -17,6 +17,7 @@ package com.keylesspalace.tusky;
import android.app.NotificationManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -58,9 +59,6 @@ public class NotificationsFragment extends SFragment implements
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
- NotificationManager notificationManager =
- (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(PullNotificationService.NOTIFY_ID);
super.onCreate(savedInstanceState);
}
@@ -119,6 +117,12 @@ public class NotificationsFragment extends SFragment implements
return rootView;
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ sendFetchNotificationsRequest();
+ }
+
@Override
public void onDestroyView() {
TabLayout tabLayout = (TabLayout) getActivity().findViewById(R.id.tab_layout);
@@ -165,6 +169,13 @@ public class NotificationsFragment extends SFragment implements
if (notifications.size() > 0 && !findNotification(notifications, fromId)) {
setFetchTimelineState(FooterViewHolder.State.LOADING);
adapter.addItems(notifications);
+
+ // Set last update id for pull notifications so that we don't get notified
+ // about things we already loaded here
+ SharedPreferences preferences = getActivity().getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putString("lastUpdateId", notifications.get(0).id);
+ editor.apply();
} else {
setFetchTimelineState(FooterViewHolder.State.END_OF_TIMELINE);
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java b/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java
deleted file mode 100644
index 745e0371..00000000
--- a/app/src/main/java/com/keylesspalace/tusky/PullNotificationService.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/* 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
- * 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;
-
-import android.app.*;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.support.annotation.Nullable;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.TaskStackBuilder;
-import android.text.Spanned;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.keylesspalace.tusky.entity.*;
-import com.keylesspalace.tusky.entity.Notification;
-import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Target;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import okhttp3.Interceptor;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import retrofit2.Call;
-import retrofit2.Callback;
-import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
-
-public class PullNotificationService extends IntentService {
- static final int NOTIFY_ID = 6; // This is an arbitrary number.
- private static final String TAG = "PullNotifications"; // logging tag
-
- public PullNotificationService() {
- super("Tusky Pull Notification Service");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- String domain = preferences.getString("domain", null);
- String accessToken = preferences.getString("accessToken", null);
- String lastUpdateId = preferences.getString("lastUpdateId", null);
- checkNotifications(domain, accessToken, lastUpdateId);
- }
-
- private void checkNotifications(final String domain, final String accessToken,
- final String lastUpdateId) {
- OkHttpClient okHttpClient = new OkHttpClient.Builder()
- .addInterceptor(new Interceptor() {
- @Override
- public okhttp3.Response intercept(Chain chain) throws IOException {
- Request originalRequest = chain.request();
-
- Request.Builder builder = originalRequest.newBuilder()
- .header("Authorization", String.format("Bearer %s", accessToken));
-
- Request newRequest = builder.build();
-
- return chain.proceed(newRequest);
- }
- })
- .build();
-
- Gson gson = new GsonBuilder()
- .registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
- .create();
-
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("https://" + domain)
- .client(okHttpClient)
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
-
- MastodonAPI api = retrofit.create(MastodonAPI.class);
-
- api.notifications(null, lastUpdateId, null).enqueue(new Callback>() {
- @Override
- public void onResponse(Call> call, retrofit2.Response> response) {
- onCheckNotificationsSuccess(response.body(), lastUpdateId);
- }
-
- @Override
- public void onFailure(Call> call, Throwable t) {
- onCheckNotificationsFailure((Exception) t);
- }
- });
- }
-
- private void onCheckNotificationsSuccess(List notifications, String lastUpdateId) {
- List mentions = new ArrayList<>();
-
- for (com.keylesspalace.tusky.entity.Notification notification : notifications) {
- if (notification.type == com.keylesspalace.tusky.entity.Notification.Type.MENTION) {
- Status status = notification.status;
-
- if (status != null) {
- MentionResult mention = new MentionResult();
- mention.content = status.content.toString();
- mention.displayName = notification.account.getDisplayName();
- mention.avatarUrl = status.account.avatar;
- mentions.add(mention);
- }
- }
- }
-
- if (notifications.size() > 0) {
- SharedPreferences preferences = getSharedPreferences(
- getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = preferences.edit();
- editor.putString("lastUpdateId", notifications.get(0).id);
- editor.apply();
- }
-
- if (mentions.size() > 0) {
- loadAvatar(mentions, mentions.get(0).avatarUrl);
- }
- }
-
- private void onCheckNotificationsFailure(Exception exception) {
- Log.e(TAG, "Failed to check notifications. " + exception.getMessage());
- }
-
- private static class MentionResult {
- String displayName;
- String content;
- String avatarUrl;
- }
-
- private String truncateWithEllipses(String string, int limit) {
- if (string.length() < limit) {
- return string;
- } else {
- return string.substring(0, limit - 3) + "...";
- }
- }
-
- private void loadAvatar(final List mentions, String url) {
- if (url != null) {
- Target target = new Target() {
- @Override
- public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
- updateNotification(mentions, bitmap);
- }
-
- @Override
- public void onBitmapFailed(Drawable errorDrawable) {
- updateNotification(mentions, null);
- }
-
- @Override
- public void onPrepareLoad(Drawable placeHolderDrawable) {}
- };
- Picasso.with(this)
- .load(url)
- .into(target);
- } else {
- updateNotification(mentions, null);
- }
- }
-
- private void updateNotification(List mentions, @Nullable Bitmap icon) {
- final int NOTIFICATION_CONTENT_LIMIT = 40;
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- String title;
- if (mentions.size() > 1) {
- title = String.format(
- getString(R.string.notification_service_several_mentions),
- mentions.size());
- } else {
- title = String.format(
- getString(R.string.notification_service_one_mention),
- mentions.get(0).displayName);
- }
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notify)
- .setContentTitle(title);
- if (icon != null) {
- builder.setLargeIcon(icon);
- }
- if (preferences.getBoolean("notificationAlertSound", true)) {
- builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI);
- }
- if (preferences.getBoolean("notificationStyleVibrate", false)) {
- builder.setVibrate(new long[] { 500, 500 });
- }
- if (preferences.getBoolean("notificationStyleLight", false)) {
- builder.setLights(0xFF00FF8F, 300, 1000);
- }
- for (int i = 0; i < mentions.size(); i++) {
- MentionResult mention = mentions.get(i);
- String text = truncateWithEllipses(mention.content, NOTIFICATION_CONTENT_LIMIT);
- builder.setContentText(text)
- .setNumber(i);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- builder.setVisibility(android.app.Notification.VISIBILITY_PRIVATE);
- builder.setCategory(android.app.Notification.CATEGORY_SOCIAL);
- }
- Intent resultIntent = new Intent(this, SplashActivity.class);
- TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
- stackBuilder.addParentStack(SplashActivity.class);
- stackBuilder.addNextIntent(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
- builder.setContentIntent(resultPendingIntent);
- NotificationManager notificationManager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(NOTIFY_ID, builder.build());
- }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/SFragment.java
index d1de088e..862e598c 100644
--- a/app/src/main/java/com/keylesspalace/tusky/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/SFragment.java
@@ -206,7 +206,6 @@ public class SFragment extends Fragment {
FragmentManager manager = getFragmentManager();
manager.beginTransaction()
- .setCustomAnimations(R.anim.zoom_in, R.anim.zoom_out, R.anim.zoom_in, R.anim.zoom_out)
.add(R.id.overlay_fragment_container, newFragment)
.addToBackStack(null)
.commit();
diff --git a/app/src/main/java/com/keylesspalace/tusky/SplashActivity.java b/app/src/main/java/com/keylesspalace/tusky/SplashActivity.java
index d35a3ce1..dcb35fdd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/SplashActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/SplashActivity.java
@@ -22,6 +22,8 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
+import android.view.Window;
+import android.view.WindowManager;
public class SplashActivity extends Activity {
private static int SPLASH_TIME_OUT = 2000;
@@ -29,7 +31,12 @@ public class SplashActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
setContentView(R.layout.activity_splash);
+
/* Determine whether the user is currently logged in, and if so go ahead and load the
* timeline. Otherwise, start the activity_login screen. */
SharedPreferences preferences = getSharedPreferences(
diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
index b8c0bfbc..76579307 100644
--- a/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/TimelineFragment.java
@@ -138,6 +138,12 @@ public class TimelineFragment extends SFragment implements
return rootView;
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ sendFetchTimelineRequest();
+ }
+
@Override
public void onDestroyView() {
if (jumpToTopAllowed()) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java b/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java
index 7cf0642d..883d56ea 100644
--- a/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/TimelinePagerAdapter.java
@@ -55,6 +55,6 @@ class TimelinePagerAdapter extends FragmentPagerAdapter {
@Override
public CharSequence getPageTitle(int position) {
- return pageTitles[position];
+ return null;
}
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java b/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java
new file mode 100644
index 00000000..2df62bc1
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/TuskyAPI.java
@@ -0,0 +1,16 @@
+package com.keylesspalace.tusky;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.POST;
+
+public interface TuskyAPI {
+ @FormUrlEncoded
+ @POST("/register")
+ Call register(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken, @Field("device_token") String deviceToken);
+ @FormUrlEncoded
+ @POST("/unregister")
+ Call unregister(@Field("instance_url") String instanceUrl, @Field("access_token") String accessToken);
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java
index 39dcef2f..84354d21 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java
@@ -21,20 +21,30 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
public class ViewTagActivity extends BaseActivity {
+ @BindView(R.id.toolbar) Toolbar toolbar;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_tag);
+ ButterKnife.bind(this);
+
String hashtag = getIntent().getStringExtra("hashtag");
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar bar = getSupportActionBar();
+
if (bar != null) {
bar.setTitle(String.format(getString(R.string.title_tag), hashtag));
+ bar.setDisplayHomeAsUpEnabled(true);
+ bar.setDisplayShowHomeEnabled(true);
}
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
@@ -42,4 +52,15 @@ public class ViewTagActivity extends BaseActivity {
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ onBackPressed();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java
index a83c35e2..de535035 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java
@@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -63,6 +64,7 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene
R.drawable.status_divider_dark);
divider.setDrawable(drawable);
recyclerView.addItemDecoration(divider);
+ recyclerView.addItemDecoration(new ConversationLineItemDecoration(context, ContextCompat.getDrawable(context, R.drawable.conversation_divider_dark)));
adapter = new ThreadAdapter(this);
recyclerView.setAdapter(adapter);
diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
index 4fd83070..1ec2fa71 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
+++ b/app/src/main/java/com/keylesspalace/tusky/ViewVideoActivity.java
@@ -16,21 +16,53 @@
package com.keylesspalace.tusky;
import android.os.Bundle;
+import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
import android.widget.MediaController;
import android.widget.VideoView;
-public class ViewVideoActivity extends AppCompatActivity {
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class ViewVideoActivity extends BaseActivity {
+ @BindView(R.id.video_player) VideoView videoView;
+ @BindView(R.id.toolbar) Toolbar toolbar;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_video);
+ ButterKnife.bind(this);
+
+ setSupportActionBar(toolbar);
+
+ ActionBar bar = getSupportActionBar();
+
+ if (bar != null) {
+ bar.setTitle(null);
+ bar.setDisplayHomeAsUpEnabled(true);
+ bar.setDisplayShowHomeEnabled(true);
+ }
+
String url = getIntent().getStringExtra("url");
- VideoView videoView = (VideoView) findViewById(R.id.video_player);
+
videoView.setVideoPath(url);
MediaController controller = new MediaController(this);
videoView.setMediaController(controller);
controller.show();
videoView.start();
}
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ onBackPressed();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
diff --git a/app/src/main/res/drawable/conversation_divider_dark.xml b/app/src/main/res/drawable/conversation_divider_dark.xml
new file mode 100644
index 00000000..42034487
--- /dev/null
+++ b/app/src/main/res/drawable/conversation_divider_dark.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_clear_24dp.xml b/app/src/main/res/drawable/ic_clear_24dp.xml
new file mode 100644
index 00000000..72af1301
--- /dev/null
+++ b/app/src/main/res/drawable/ic_clear_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_home_24dp.xml b/app/src/main/res/drawable/ic_home_24dp.xml
new file mode 100644
index 00000000..55e6239b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_24dp.xml b/app/src/main/res/drawable/ic_notifications_24dp.xml
new file mode 100644
index 00000000..86ed9486
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_public_24dp.xml b/app/src/main/res/drawable/ic_public_24dp.xml
index d976b424..f66427b1 100644
--- a/app/src/main/res/drawable/ic_public_24dp.xml
+++ b/app/src/main/res/drawable/ic_public_24dp.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
diff --git a/app/src/main/res/layout/activity_view_thread.xml b/app/src/main/res/layout/activity_view_thread.xml
index d808ea49..e714bf43 100644
--- a/app/src/main/res/layout/activity_view_thread.xml
+++ b/app/src/main/res/layout/activity_view_thread.xml
@@ -25,14 +25,12 @@
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_view_video.xml b/app/src/main/res/layout/activity_view_video.xml
index 77001cba..38e3cfab 100644
--- a/app/src/main/res/layout/activity_view_video.xml
+++ b/app/src/main/res/layout/activity_view_video.xml
@@ -1,14 +1,24 @@
-
+ android:background="@color/view_video_background"
+ tools:context=".ViewVideoActivity">
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_view_media.xml b/app/src/main/res/layout/fragment_view_media.xml
index 1126dbe0..d3b84def 100644
--- a/app/src/main/res/layout/fragment_view_media.xml
+++ b/app/src/main/res/layout/fragment_view_media.xml
@@ -2,6 +2,7 @@
-
-
+
-
+
-
- android:layout_centerVertical="true"
- android:layout_toRightOf="@id/account_avatar">
+
-
+
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_blocked_user.xml b/app/src/main/res/layout/item_blocked_user.xml
index 1d2a4a44..b1d2b4d5 100644
--- a/app/src/main/res/layout/item_blocked_user.xml
+++ b/app/src/main/res/layout/item_blocked_user.xml
@@ -1,60 +1,57 @@
+ android:layout_height="72dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:gravity="center_vertical">
-
+ android:layout_alignParentLeft="true"
+ android:layout_marginRight="24dp"
+ android:layout_centerVertical="true"/>
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index c01d532c..c2babd78 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -20,4 +20,5 @@
8dp
8dp
8dp
+ 38dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 006f4999..65166c0e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -34,7 +34,7 @@
Follows
Followers
Favourites
- Blocked Users
+ Blocked users
\@%s
%s boosted
@@ -50,8 +50,8 @@
end of the notifications
end of the accounts
- %s boosted your status
- %s favourited your status
+ %s boosted your toot
+ %s favourited your toot
%s followed you
Report @%s
@@ -129,5 +129,7 @@
Search accounts…
NSFW
Mention
+ http://tusky.zeonfederated.com
+ %s mentioned you
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 9cedad58..9ace1a1e 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -3,39 +3,20 @@
android:key="@string/preferences_file_key">
-
-
-
-
-
+ android:defaultValue="true" />
+ android:defaultValue="true" />
diff --git a/build.gradle b/build.gradle
index 83a6ca5a..22983d58 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
+ classpath 'com.google.gms:google-services:3.0.0'
}
}