Dark and light themes now exist and can be switched in preferences.

This commit is contained in:
Vavassor 2017-02-16 13:52:55 -05:00
parent 79b3d83368
commit 96a5692717
30 changed files with 408 additions and 252 deletions

View File

@ -25,7 +25,6 @@ import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.method.LinkMovementMethod;
import android.util.TypedValue;
@ -50,7 +49,7 @@ import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class AccountActivity extends AppCompatActivity {
public class AccountActivity extends BaseActivity {
private static final String TAG = "AccountActivity"; // logging tag
private String domain;

View File

@ -0,0 +1,58 @@
/* 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
* <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Menu;
/* There isn't presently a way to globally change the theme of a whole application at runtime, just
* individual activities. So, each activity has to set its theme before any views are created. And
* the most expedient way to accomplish this was to put it in a base class and just have every
* activity extend from it. */
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("lightTheme", false)) {
setTheme(R.style.AppTheme_Light);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
TypedValue value = new TypedValue();
int color;
if (getTheme().resolveAttribute(R.attr.toolbar_icon_tint, value, true)) {
color = value.data;
} else {
color = Color.WHITE;
}
for (int i = 0; i < menu.size(); i++) {
Drawable icon = menu.getItem(i).getIcon();
if (icon != null) {
icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
}
return super.onCreateOptionsMenu(menu);
}
}

View File

@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@ -40,12 +41,12 @@ import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.TypedValue;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
@ -71,18 +72,14 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ComposeActivity extends AppCompatActivity {
public class ComposeActivity extends BaseActivity {
private static final int STATUS_CHARACTER_LIMIT = 500;
private static final int STATUS_MEDIA_SIZE_LIMIT = 4000000; // 4MB
private static final int MEDIA_PICK_RESULT = 1;
@ -103,64 +100,28 @@ public class ComposeActivity extends AppCompatActivity {
private View contentWarningBar;
private static class QueuedMedia {
public enum Type {
enum Type {
IMAGE,
VIDEO
}
public enum ReadyStage {
enum ReadyStage {
DOWNSIZING,
UPLOADING,
}
private Type type;
private ImageView preview;
private Uri uri;
private String id;
private ReadyStage readyStage;
private byte[] content;
Type type;
ImageView preview;
Uri uri;
String id;
ReadyStage readyStage;
byte[] content;
public QueuedMedia(Type type, Uri uri, ImageView preview) {
QueuedMedia(Type type, Uri uri, ImageView preview) {
this.type = type;
this.uri = uri;
this.preview = preview;
}
public Type getType() {
return type;
}
public ImageView getPreview() {
return preview;
}
public Uri getUri() {
return uri;
}
public String getId() {
return id;
}
public byte[] getContent() {
return content;
}
public ReadyStage getReadyStage() {
return readyStage;
}
public void setId(String id) {
this.id = id;
}
public void setReadyStage(ReadyStage readyStage) {
this.readyStage = readyStage;
}
public void setContent(byte[] content) {
this.content = content;
}
}
private void doErrorDialog(int descriptionId, int actionId, View.OnClickListener listener) {
@ -175,10 +136,10 @@ public class ComposeActivity extends AppCompatActivity {
}
private static class FindCharsResult {
public int charIndex;
public int stringIndex;
int charIndex;
int stringIndex;
public FindCharsResult() {
FindCharsResult() {
charIndex = -1;
stringIndex = -1;
}
@ -281,6 +242,15 @@ public class ComposeActivity extends AppCompatActivity {
}
}
private static int getThemeColor(Context context, int attribute) {
TypedValue value = new TypedValue();
if (context.getTheme().resolveAttribute(attribute, value, true)) {
return value.data;
} else {
return android.R.color.black;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -300,7 +270,7 @@ public class ComposeActivity extends AppCompatActivity {
textEditor = (EditText) findViewById(R.id.field_status);
final TextView charactersLeft = (TextView) findViewById(R.id.characters_left);
final int mentionColour = ContextCompat.getColor(this, R.color.compose_mention);
final int mentionColour = getThemeColor(this, R.attr.compose_mention_color);
TextWatcher textEditorWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
@ -414,7 +384,7 @@ public class ComposeActivity extends AppCompatActivity {
}
JSONArray media_ids = new JSONArray();
for (QueuedMedia item : mediaQueued) {
media_ids.put(item.getId());
media_ids.put(item.id);
}
if (media_ids.length() > 0) {
parameters.put("media_ids", media_ids);
@ -429,12 +399,13 @@ public class ComposeActivity extends AppCompatActivity {
public void onResponse(JSONObject response) {
onSendSuccess();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onSendFailure();
}
}) {
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onSendFailure();
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
@ -454,8 +425,8 @@ public class ComposeActivity extends AppCompatActivity {
textEditor.setError(getString(R.string.error_sending_status));
}
private void readyStatus(final String content, final String visibility,
final boolean sensitive, final String spoilerText) {
private void readyStatus(final String content, final String visibility, final boolean sensitive,
final String spoilerText) {
final ProgressDialog dialog = ProgressDialog.show(
this, getString(R.string.dialog_title_finishing_media_upload),
getString(R.string.dialog_message_uploading_media), true, true);
@ -570,7 +541,7 @@ public class ComposeActivity extends AppCompatActivity {
private void addMediaToQueue(QueuedMedia.Type type, Bitmap preview, Uri uri, long mediaSize) {
final QueuedMedia item = new QueuedMedia(type, uri, new ImageView(this));
ImageView view = item.getPreview();
ImageView view = item.preview;
Resources resources = getResources();
int side = resources.getDimensionPixelSize(R.dimen.compose_media_preview_side);
int margin = resources.getDimensionPixelSize(R.dimen.compose_media_preview_margin);
@ -598,7 +569,7 @@ public class ComposeActivity extends AppCompatActivity {
textEditor.setPadding(textEditor.getPaddingLeft(), textEditor.getPaddingTop(),
textEditor.getPaddingRight(), totalHeight);
// If there's one video in the queue it is full, so disable the button to queue more.
if (item.getType() == QueuedMedia.Type.VIDEO) {
if (item.type == QueuedMedia.Type.VIDEO) {
disableMediaPicking();
}
} else if (queuedCount >= Status.MAX_MEDIA_ATTACHMENTS) {
@ -617,7 +588,7 @@ public class ComposeActivity extends AppCompatActivity {
}
private void removeMediaFromQueue(QueuedMedia item) {
mediaPreviewBar.removeView(item.getPreview());
mediaPreviewBar.removeView(item.preview);
mediaQueued.remove(item);
if (mediaQueued.size() == 0) {
showMarkSensitive(false);
@ -639,10 +610,10 @@ public class ComposeActivity extends AppCompatActivity {
}
private void downsizeMedia(final QueuedMedia item) {
item.setReadyStage(QueuedMedia.ReadyStage.DOWNSIZING);
item.readyStage = QueuedMedia.ReadyStage.DOWNSIZING;
InputStream stream;
try {
stream = getContentResolver().openInputStream(item.getUri());
stream = getContentResolver().openInputStream(item.uri);
} catch (FileNotFoundException e) {
onMediaDownsizeFailure(item);
return;
@ -652,7 +623,7 @@ public class ComposeActivity extends AppCompatActivity {
new DownsizeImageTask(STATUS_MEDIA_SIZE_LIMIT, new DownsizeImageTask.Listener() {
@Override
public void onSuccess(List<byte[]> contentList) {
item.setContent(contentList.get(0));
item.content = contentList.get(0);
uploadMedia(item);
}
@ -695,7 +666,7 @@ public class ComposeActivity extends AppCompatActivity {
}
private void uploadMedia(final QueuedMedia item) {
item.setReadyStage(QueuedMedia.ReadyStage.UPLOADING);
item.readyStage = QueuedMedia.ReadyStage.UPLOADING;
String endpoint = getString(R.string.endpoint_media);
String url = "https://" + domain + endpoint;
@ -714,7 +685,7 @@ public class ComposeActivity extends AppCompatActivity {
@Override
public void onResponse(JSONObject response) {
try {
item.setId(response.getString("id"));
item.id = response.getString("id");
} catch (JSONException e) {
onUploadFailure(item);
return;
@ -736,11 +707,11 @@ public class ComposeActivity extends AppCompatActivity {
@Override
public DataItem getData() {
byte[] content = item.getContent();
byte[] content = item.content;
if (content == null) {
InputStream stream;
try {
stream = getContentResolver().openInputStream(item.getUri());
stream = getContentResolver().openInputStream(item.uri);
} catch (FileNotFoundException e) {
return null;
}
@ -758,7 +729,7 @@ public class ComposeActivity extends AppCompatActivity {
return data;
}
};
request.addMarker("media_" + item.getUri().toString());
request.addMarker("media_" + item.uri.toString());
VolleySingleton.getInstance(this).addToRequestQueue(request);
}
@ -768,8 +739,8 @@ public class ComposeActivity extends AppCompatActivity {
}
private void cancelReadyingMedia(QueuedMedia item) {
if (item.getReadyStage() == QueuedMedia.ReadyStage.UPLOADING) {
VolleySingleton.getInstance(this).cancelRequest("media_" + item.getUri().toString());
if (item.readyStage == QueuedMedia.ReadyStage.UPLOADING) {
VolleySingleton.getInstance(this).cancelRequest("media_" + item.uri.toString());
}
waitForMediaLatch.countDown();
}
@ -799,7 +770,7 @@ public class ComposeActivity extends AppCompatActivity {
return;
}
if (mediaQueued.size() > 0
&& mediaQueued.get(0).getType() == QueuedMedia.Type.IMAGE) {
&& mediaQueued.get(0).type == QueuedMedia.Type.IMAGE) {
displayTransientError(R.string.error_media_upload_image_or_video);
return;
}

View File

@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
@ -42,7 +41,7 @@ import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
public class LoginActivity extends AppCompatActivity {
public class LoginActivity extends BaseActivity {
private static final String TAG = "LoginActivity";
private static String OAUTH_SCOPES = "read write follow";
@ -279,7 +278,7 @@ public class LoginActivity extends AppCompatActivity {
errorText.setText(error);
} else {
// This case means a junk response was received somehow.
errorText.setText("An unidentified authorization error occurred.");
errorText.setText(getString(R.string.error_authorization_unknown));
}
}
}

View File

@ -24,10 +24,8 @@ import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
@ -43,7 +41,7 @@ import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity"; // logging tag
private AlarmManager alarmManager;
@ -102,20 +100,6 @@ public class MainActivity extends AppCompatActivity {
} else {
alarmManager.cancel(serviceAlarmIntent);
}
/* @Unused: for Firebase Push Notifications
Log.d(TAG, "token " + FirebaseInstanceId.getInstance().getToken());
// Check if it's necessary to register for push notifications for this instance.
boolean registered = preferences.getBoolean("firebaseRegistered", false);
if (!registered) {
String registrationId = preferences.getString("firebaseRegistrationId", null);
if (registrationId == null) {
registrationId = FirebaseInstanceId.getInstance().getToken();
}
sendRegistrationToServer(registrationId, true);
}
*/
}
private void fetchUserInfo() {
@ -180,71 +164,6 @@ public class MainActivity extends AppCompatActivity {
Log.e(TAG, "Failed to fetch user info. " + exception.getMessage());
}
/* @Unused: For Firebase push notifications, useless for now.
private void sendRegistrationToServer(String token, final boolean register) {
SharedPreferences preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
String domain = preferences.getString("domain", null);
final String accessToken = preferences.getString("accessToken", null);
String endpoint;
if (register) {
endpoint = getString(R.string.endpoint_devices_register);
} else {
endpoint = getString(R.string.endpoint_devices_unregister);
}
String url = "https://" + domain + endpoint;
JSONObject formData = new JSONObject();
try {
formData.put("registration_id", token);
} catch (JSONException e) {
onSendRegistrationToServerFailure();
return;
}
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, url, formData,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
onSendRegistrationToServerSuccess(response, register);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onSendRegistrationToServerFailure();
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + accessToken);
return headers;
}
};
VolleySingleton.getInstance(this).addToRequestQueue(request);
}
private void onSendRegistrationToServerSuccess(JSONObject response, boolean register) {
String registeredWord;
if (register) {
registeredWord = "registration";
} else {
registeredWord = "unregistration";
}
Log.d(TAG, String.format("Firebase %s is confirmed with the Mastodon instance. %s",
registeredWord, response.toString()));
SharedPreferences preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("firebaseRegistered", register);
editor.apply();
}
private void onSendRegistrationToServerFailure() {
Log.d(TAG, "Firebase registration with the Mastodon instance failed");
}
*/
private void compose() {
Intent intent = new Intent(this, ComposeActivity.class);
startActivity(intent);

View File

@ -15,16 +15,79 @@
package com.keylesspalace.tusky;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
public class PreferencesActivity extends AppCompatActivity {
public class PreferencesActivity extends AppCompatActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
private boolean themeSwitched;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
themeSwitched = savedInstanceState.getBoolean("themeSwitched");
} else {
Bundle extras = getIntent().getExtras();
if (extras != null) {
themeSwitched = extras.getBoolean("themeSwitched");
} else {
themeSwitched = false;
}
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
if (preferences.getBoolean("lightTheme", false)) {
setTheme(R.style.AppTheme_Light);
}
preferences.registerOnSharedPreferenceChangeListener(this);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new PreferencesFragment())
.commit();
}
private void saveInstanceState(Bundle outState) {
outState.putBoolean("themeSwitched", themeSwitched);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
saveInstanceState(outState);
super.onSaveInstanceState(outState);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("lightTheme")) {
themeSwitched = true;
// recreate() could be used instead, but it doesn't have an animation B).
Intent intent = getIntent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle savedInstanceState = new Bundle();
saveInstanceState(savedInstanceState);
intent.putExtras(savedInstanceState);
startActivity(intent);
finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
}
@Override
public void onBackPressed() {
/* Switching themes won't actually change the theme of activities on the back stack.
* Either the back stack activities need to all be recreated, or do the easier thing, which
* is hijack the back button press and use it to launch a new MainActivity and clear the
* back stack. */
if (themeSwitched) {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
} else {
super.onBackPressed();
}
}
}

View File

@ -20,7 +20,6 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
public class SplashActivity extends AppCompatActivity {
@Override

View File

@ -43,7 +43,7 @@ public class Status {
private Spanned content;
/** the fully-qualified url of the avatar image */
private String avatar;
private String rebloggedByUsername;
private String rebloggedByDisplayName;
/** when the status was initially created */
private Date createdAt;
/** whether the authenticated user has reblogged this status */
@ -104,8 +104,8 @@ public class Status {
return createdAt;
}
public String getRebloggedByUsername() {
return rebloggedByUsername;
public String getRebloggedByDisplayName() {
return rebloggedByDisplayName;
}
public boolean getReblogged() {
@ -136,8 +136,8 @@ public class Status {
return mentions;
}
public void setRebloggedByUsername(String name) {
rebloggedByUsername = name;
public void setRebloggedByDisplayName(String name) {
rebloggedByDisplayName = name;
}
public void setReblogged(boolean reblogged) {
@ -257,7 +257,7 @@ public class Status {
Status status;
if (reblog != null) {
status = reblog;
status.setRebloggedByUsername(username);
status.setRebloggedByDisplayName(displayName);
} else {
Spanned contentPlus = HtmlUtils.fromHtml(content);
status = new Status(

View File

@ -26,7 +26,6 @@ import android.text.style.URLSpan;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
@ -43,7 +42,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
private TextView content;
private NetworkImageView avatar;
private View rebloggedBar;
private TextView rebloggedByUsername;
private TextView rebloggedByDisplayName;
private ImageButton replyButton;
private StatusButton reblogButton;
private StatusButton favouriteButton;
@ -70,7 +69,7 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
avatar.setDefaultImageResId(R.drawable.avatar_default);
avatar.setErrorImageResId(R.drawable.avatar_error);
rebloggedBar = itemView.findViewById(R.id.status_reblogged_bar);
rebloggedByUsername = (TextView) itemView.findViewById(R.id.status_reblogged);
rebloggedByDisplayName = (TextView) itemView.findViewById(R.id.status_reblogged);
replyButton = (ImageButton) itemView.findViewById(R.id.status_reply);
reblogButton = (StatusButton) itemView.findViewById(R.id.status_reblog);
favouriteButton = (StatusButton) itemView.findViewById(R.id.status_favourite);
@ -174,15 +173,15 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
sinceCreated.setText(readout);
}
public void setRebloggedByUsername(String name) {
Context context = rebloggedByUsername.getContext();
public void setRebloggedByDisplayName(String name) {
Context context = rebloggedByDisplayName.getContext();
String format = context.getString(R.string.status_boosted_format);
String boostedText = String.format(format, name);
rebloggedByUsername.setText(boostedText);
rebloggedByDisplayName.setText(boostedText);
rebloggedBar.setVisibility(View.VISIBLE);
}
public void hideRebloggedByUsername() {
public void hideRebloggedByDisplayName() {
rebloggedBar.setVisibility(View.GONE);
}
@ -332,11 +331,11 @@ public class StatusViewHolder extends RecyclerView.ViewHolder {
setAvatar(status.getAvatar());
setReblogged(status.getReblogged());
setFavourited(status.getFavourited());
String rebloggedByUsername = status.getRebloggedByUsername();
if (rebloggedByUsername == null) {
hideRebloggedByUsername();
String rebloggedByDisplayName = status.getRebloggedByDisplayName();
if (rebloggedByDisplayName == null) {
hideRebloggedByDisplayName();
} else {
setRebloggedByUsername(rebloggedByUsername);
setRebloggedByDisplayName(rebloggedByDisplayName);
}
Status.MediaAttachment[] attachments = status.getAttachments();
boolean sensitive = status.getSensitive();

View File

@ -20,10 +20,9 @@ import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
public class ViewTagActivity extends AppCompatActivity {
public class ViewTagActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -21,13 +21,11 @@ import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
public class ViewThreadActivity extends AppCompatActivity {
public class ViewThreadActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:fromAlpha="0"
android:toAlpha="1"
android:duration="300" />

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:fromAlpha="1"
android:toAlpha="0"
android:duration="300" />

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:color="#BFAF00" app:state_marked="true" />
<item android:color="#4F4F4F" />
</selector>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#8F8F8F" android:state_enabled="false" />
<item android:color="#000000" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:color="#009F6F" app:state_marked="true" />
<item android:color="#BFBFBF" android:state_enabled="false" />
<item android:color="#4F4F4F" />
</selector>

View File

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/media_preview_unloaded_background" />
<solid android:color="?attr/media_preview_unloaded_background_color" />
<stroke
android:dashWidth="4dp"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffafafaf" />
<solid android:color="#ff4f575f" />
<corners android:radius="2dp" />
</shape>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffefefef" />
<corners android:radius="2dp" />
</shape>

View File

@ -79,7 +79,7 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:background="?attr/toolbar_background_color"
android:layout_gravity="top"
app:layout_collapseMode="pin" />

View File

@ -10,8 +10,8 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:elevation="4dp" />
android:elevation="4dp"
android:background="?attr/toolbar_background_color" />
<RelativeLayout
android:layout_width="match_parent"

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -20,8 +19,8 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:elevation="4dp" />
android:elevation="4dp"
android:background="?attr/toolbar_background_color" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"

View File

@ -15,8 +15,8 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:elevation="4dp" />
android:elevation="4dp"
android:background="?attr/toolbar_background_color" />
<FrameLayout
android:id="@+id/fragment_container"

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -16,8 +15,8 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:elevation="4dp" />
android:elevation="4dp"
android:background="?attr/toolbar_background_color" />
<FrameLayout
android:id="@+id/fragment_container"

View File

@ -19,7 +19,8 @@
app:srcCompat="@drawable/ic_reblogged"
android:id="@+id/status_reblogged_icon"
android:paddingRight="@dimen/status_avatar_padding"
android:paddingLeft="@dimen/status_reblogged_icon_left_padding" />
android:paddingLeft="@dimen/status_reblogged_icon_left_padding"
android:tint="?attr/notification_icon_tint" />
<TextView
android:layout_width="wrap_content"
@ -121,7 +122,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/status_media_preview_top_margin"
android:padding="8dp"
android:background="@color/sensitive_media_warning_background"
android:background="?attr/sensitive_media_warning_background_color"
android:orientation="vertical"
android:visibility="gone">
@ -182,6 +183,7 @@
android:layout_height="@dimen/status_media_preview_height"
android:layout_weight="1"
android:scaleType="centerCrop" />
</LinearLayout>
</LinearLayout>
@ -198,9 +200,9 @@
<ImageButton
app:srcCompat="@drawable/ic_reply"
android:id="@+id/status_reply"
style="?attr/image_button_style"
android:layout_width="32dp"
android:layout_height="32dp" />
android:layout_height="32dp"
style="?attr/image_button_style" />
<Space
android:layout_width="0dp"
@ -210,7 +212,7 @@
<com.keylesspalace.tusky.StatusButton
app:srcCompat="@drawable/ic_reblog"
android:id="@+id/status_reblog"
style="?attr/reblog_button_style"
style="?attr/status_reblog_button_style"
android:layout_width="32dp"
android:layout_height="32dp" />
@ -222,7 +224,7 @@
<com.keylesspalace.tusky.StatusButton
android:layout_width="32dp"
android:layout_height="32dp"
style="?attr/favourite_button_style"
style="?attr/status_favourite_button_style"
app:srcCompat="@drawable/ic_favourite"
android:id="@+id/status_favourite" />

View File

@ -11,16 +11,21 @@
</declare-styleable>
<!--Themed Attributes-->
<attr name="toolbar_background_color" format="reference" />
<attr name="toolbar_icon_tint" format="reference" />
<attr name="image_button_style" format="reference" />
<attr name="favourite_button_style" format="reference" />
<attr name="reblog_button_style" format="reference" />
<attr name="status_favourite_button_style" format="reference" />
<attr name="status_reblog_button_style" format="reference" />
<attr name="content_warning_button" format="reference" />
<attr name="notification_content" format="reference" />
<attr name="notification_icon_tint" format="reference|color" />
<attr name="sensitive_media_warning_background_color" format="reference|color" />
<attr name="media_preview_unloaded_background_color" format="reference|color" />
<attr name="status_text_color_secondary" format="reference|color" />
<attr name="status_divider_color" format="reference|color" />
<attr name="tab_page_margin_color" format="reference|color" />
<attr name="notification_content" format="reference" />
<attr name="notification_icon_tint" format="reference|color" />
<attr name="account_header_background_color" format="reference|color" />
<attr name="compose_media_button_tint" format="reference|color" />
<attr name="compose_mention_color" format="reference|color" />
</resources>

View File

@ -1,21 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#44A673</color>
<color name="colorPrimaryDark">#2C996E</color>
<color name="colorAccent">#3F8A65</color>
<color name="colorBackground">#3C4945</color>
<color name="windowBackground">#191E1E</color>
<!--These colors are not affected by themes.-->
<color name="splash_background">#4F4F4F</color>
<color name="button_dark">#3F8A65</color>
<color name="image_button_dark">#CFCFCF</color>
<color name="view_video_background">#000000</color>
<color name="sensitive_media_warning_background">#303030</color>
<color name="media_preview_unloaded_background">#2F2F2F</color>
<color name="compose_mention">#AFBFCF</color>
<color name="notification_content_faded">#9F9F9F</color>
<color name="notification_icon_tint">#CFCFCF</color>
<!--Dark Theme Colors-->
<color name="color_primary_dark">#44A673</color>
<color name="color_primary_dark_dark">#2C996E</color> <!--Dark Dark-->
<color name="color_accent_dark">#3F8A65</color>
<color name="button_dark">#3F8A65</color>
<color name="color_background_dark">#3C4945</color>
<color name="window_background_dark">#191E1E</color>
<color name="edit_text_color_dark">#FFFFFF</color>
<color name="text_color_primary_dark">#FFFFFF</color>
<color name="text_color_secondary_dark">#FFFFFF</color>
<color name="text_color_tertiary_dark">#FFFFFF</color>
<color name="text_color_primary_inverse_dark">#000000</color>
<color name="text_color_secondary_inverse_dark">#000000</color>
<color name="text_color_tertiary_inverse_dark">#000000</color>
<color name="toolbar_background_dark">#44A673</color>
<color name="toolbar_icon_dark">#FFFFFF</color>
<color name="image_button_dark">#CFCFCF</color>
<color name="sensitive_media_warning_background_dark">#303030</color>
<color name="media_preview_unloaded_background_dark">#2F2F2F</color>
<color name="status_text_secondary_dark">#A4B4BC</color>
<color name="status_divider_dark">#000000</color>
<color name="tab_page_margin_dark">#4C534B</color>
<color name="account_header_background_dark">#000000</color>
<color name="compose_mention_dark">#AFBFCF</color>
<color name="notification_content_faded_dark">#9F9F9F</color>
<color name="notification_icon_tint_dark">#CFCFCF</color>
<!--Light Theme Colors-->
<color name="color_primary_light">#44A673</color>
<color name="color_primary_dark_light">#2C996E</color>
<color name="color_accent_light">#3F8A65</color>
<color name="button_light">#79daa8</color>
<color name="color_background_light">#FFFFFF</color>
<color name="window_background_light">#FFFFFF</color>
<color name="edit_text_color_light">#000000</color>
<color name="text_color_primary_light">#000000</color>
<color name="text_color_secondary_light">#000000</color>
<color name="text_color_tertiary_light">#000000</color>
<color name="text_color_primary_inverse_light">#FFFFFF</color>
<color name="text_color_secondary_inverse_light">#FFFFFF</color>
<color name="text_color_tertiary_inverse_light">#FFFFFF</color>
<color name="toolbar_background_light">#f6f7f7</color>
<color name="toolbar_icon_light">#000000</color>
<color name="image_button_light">#2F2F2F</color>
<color name="sensitive_media_warning_background_light">#B0B0B0</color>
<color name="media_preview_unloaded_background_light">#CFCFCF</color>
<color name="status_text_secondary_light">#34444C</color>
<color name="status_divider_light">#CFCFCF</color>
<color name="tab_page_margin_light">#9F9F9F</color>
<color name="account_header_background_light">#EFEFEF</color>
<color name="compose_mention_light">#2F5F6F</color>
<color name="notification_content_faded_light">#7F7F7F</color>
<color name="notification_icon_tint_light">#1F1F1F</color>
</resources>

View File

@ -41,6 +41,7 @@
<string name="endpoint_devices_register">/api/v1/devices/register</string>
<string name="endpoint_devices_unregister">/api/v1/devices/unregister</string>
<string name="error_authorization_unknown">An unidentified authorization error occurred.</string>
<string name="error_fetching_notifications">Notifications could not be fetched.</string>
<string name="error_compose_character_limit">The status is too long!</string>
<string name="error_sending_status">The status failed to be sent.</string>
@ -135,5 +136,7 @@
<string name="pref_title_notification_alert_sound">Notify with a sound</string>
<string name="pref_title_notification_style_vibrate">Notify with vibration</string>
<string name="pref_title_notification_style_light">Notify with light</string>
<string name="pref_title_appearance_settings">Appearance</string>
<string name="pref_title_light_theme">Use The Light Theme</string>
</resources>

View File

@ -1,56 +1,117 @@
<resources>
<!-- Base application theme (Dark). -->
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/splash_background</item>
</style>
<!--Base Application Theme Styles (Dark)-->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorPrimary">@color/color_primary_dark</item>
<item name="colorPrimaryDark">@color/color_primary_dark_dark</item>
<item name="colorAccent">@color/color_accent_dark</item>
<item name="colorButtonNormal">@color/button_dark</item>
<item name="android:colorBackground">@color/colorBackground</item>
<item name="android:windowBackground">@color/windowBackground</item>
<item name="android:editTextColor">#FFFFFF</item>
<item name="android:textColorPrimary">#FFFFFF</item>
<item name="android:textColorSecondary">#FFFFFF</item>
<item name="android:textColorTertiary">#FFFFFF</item>
<item name="android:textColorPrimaryInverse">#000000</item>
<item name="android:textColorSecondaryInverse">#000000</item>
<item name="android:textColorTertiaryInverse">#000000</item>
<item name="android:colorBackground">@color/color_background_dark</item>
<item name="android:windowBackground">@color/window_background_dark</item>
<item name="android:editTextColor">@color/edit_text_color_dark</item>
<item name="android:textColorPrimary">@color/text_color_primary_dark</item>
<item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
<item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
<item name="android:textColorPrimaryInverse">@color/text_color_primary_inverse_dark</item>
<item name="android:textColorSecondaryInverse">@color/text_color_secondary_inverse_dark</item>
<item name="android:textColorTertiaryInverse">@color/text_color_tertiary_inverse_dark</item>
<item name="bottomSheetDialogTheme">@style/AppTheme.BottomSheetDialog.Dark</item>
<item name="notification_content">@color/notification_content_faded</item>
<item name="notification_icon_tint">@color/notification_icon_tint</item>
<item name="toolbar_background_color">@color/toolbar_background_dark</item>
<item name="toolbar_icon_tint">@color/toolbar_icon_dark</item>
<item name="image_button_style">@style/AppTheme.ImageButton.Dark</item>
<item name="favourite_button_style">@style/AppTheme.FavouriteButton.Dark</item>
<item name="reblog_button_style">@style/AppTheme.ReblogButton.Dark</item>
<item name="status_reblog_button_style">@style/AppTheme.ReblogButton.Dark</item>
<item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Dark</item>
<item name="content_warning_button">@drawable/toggle_small</item>
<item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_dark</item>
<item name="media_preview_unloaded_background_color">@color/media_preview_unloaded_background_dark</item>
<item name="status_text_color_secondary">@color/status_text_secondary_dark</item>
<item name="status_divider_color">@color/status_divider_dark</item>
<item name="tab_page_margin_color">@color/tab_page_margin_dark</item>
<item name="account_header_background_color">@color/account_header_background_dark</item>
<item name="compose_media_button_tint">@color/media_button_dark</item>
<item name="compose_mention_color">@color/compose_mention_dark</item>
<item name="notification_content">@color/notification_content_faded_dark</item>
<item name="notification_icon_tint">@color/notification_icon_tint_dark</item>
</style>
<style name="AppTheme.ImageButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/image_button_dark</item>
</style>
<style name="AppTheme.FavouriteButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/favourite_button_dark</item>
</style>
<style name="AppTheme.ReblogButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/reblog_button_dark</item>
</style>
<style name="AppTheme.BottomSheetDialog.Dark" parent="@style/Theme.Design.BottomSheetDialog">
<item name="colorAccent">@color/colorAccent</item>
<item name="android:colorBackground">@color/colorBackground</item>
<style name="AppTheme.FavouriteButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/favourite_button_dark</item>
</style>
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/splash_background</item>
<style name="AppTheme.BottomSheetDialog.Dark" parent="@style/Theme.Design.BottomSheetDialog">
<item name="colorAccent">@color/color_accent_dark</item>
<item name="android:colorBackground">@color/color_background_dark</item>
</style>
<!--Light Application Theme Styles-->
<style name="AppTheme.Light" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/color_primary_light</item>
<item name="colorPrimaryDark">@color/color_primary_dark_light</item>
<item name="colorAccent">@color/color_accent_light</item>
<item name="colorButtonNormal">@color/button_light</item>
<item name="android:colorBackground">@color/color_background_light</item>
<item name="android:windowBackground">@color/window_background_light</item>
<item name="android:editTextColor">@color/edit_text_color_light</item>
<item name="android:textColorPrimary">@color/text_color_primary_light</item>
<item name="android:textColorSecondary">@color/text_color_secondary_light</item>
<item name="android:textColorTertiary">@color/text_color_tertiary_light</item>
<item name="android:textColorPrimaryInverse">@color/text_color_primary_inverse_light</item>
<item name="android:textColorSecondaryInverse">@color/text_color_secondary_inverse_light</item>
<item name="android:textColorTertiaryInverse">@color/text_color_tertiary_inverse_light</item>
<item name="bottomSheetDialogTheme">@style/AppTheme.BottomSheetDialog.Light</item>
<item name="toolbar_background_color">@color/toolbar_background_light</item>
<item name="toolbar_icon_tint">@color/toolbar_icon_light</item>
<item name="image_button_style">@style/AppTheme.ImageButton.Light</item>
<item name="status_reblog_button_style">@style/AppTheme.ReblogButton.Light</item>
<item name="status_favourite_button_style">@style/AppTheme.FavouriteButton.Light</item>
<item name="content_warning_button">@drawable/toggle_small_light</item>
<item name="sensitive_media_warning_background_color">@color/sensitive_media_warning_background_light</item>
<item name="media_preview_unloaded_background_color">@color/media_preview_unloaded_background_light</item>
<item name="status_text_color_secondary">@color/status_text_secondary_light</item>
<item name="status_divider_color">@color/status_divider_light</item>
<item name="tab_page_margin_color">@color/tab_page_margin_light</item>
<item name="account_header_background_color">@color/account_header_background_light</item>
<item name="compose_media_button_tint">@color/media_button_light</item>
<item name="compose_mention_color">@color/compose_mention_light</item>
<item name="notification_content">@color/notification_content_faded_light</item>
<item name="notification_icon_tint">@color/notification_icon_tint_light</item>
</style>
<style name="AppTheme.ImageButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/image_button_light</item>
</style>
<style name="AppTheme.ReblogButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/reblog_button_light</item>
</style>
<style name="AppTheme.FavouriteButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:tint">@color/favourite_button_light</item>
</style>
<style name="AppTheme.BottomSheetDialog.Light" parent="@style/Theme.Design.Light.BottomSheetDialog">
<item name="colorAccent">@color/color_accent_light</item>
<item name="android:colorBackground">@color/color_background_light</item>
</style>
</resources>

View File

@ -2,8 +2,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="@string/preferences_file_key">
<PreferenceCategory
android:title="@string/pref_title_notification_settings">
<PreferenceCategory android:title="@string/pref_title_notification_settings">
<CheckBoxPreference
android:key="pullNotifications"
@ -40,4 +39,13 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_appearance_settings">
<CheckBoxPreference
android:key="lightTheme"
android:title="@string/pref_title_light_theme"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>