Implement Dark Theme (DayNight with automatic option)

This is just a first rough implementation.
A real UI designer should look over this.
This commit is contained in:
Torsten Grote
2018-05-24 11:52:13 -03:00
parent dcacae0729
commit 3c6b43b2bd
116 changed files with 619 additions and 328 deletions

View File

@@ -19,6 +19,7 @@ import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.reporting.BriarReportPrimer;
import org.briarproject.briar.android.reporting.BriarReportSenderFactory;
import org.briarproject.briar.android.reporting.DevReportActivity;
import org.briarproject.briar.android.util.UiUtils;
import java.util.Collection;
import java.util.logging.Handler;
@@ -85,6 +86,7 @@ public class BriarApplicationImpl extends Application
Localizer.initialize(prefs);
super.attachBaseContext(
Localizer.getInstance().setLocale(base));
setTheme(base, prefs);
ACRA.init(this);
}
@@ -123,6 +125,17 @@ public class BriarApplicationImpl extends Application
Localizer.getInstance().setLocale(this);
}
private void setTheme(Context ctx, SharedPreferences prefs) {
String theme = prefs.getString("pref_key_theme", null);
if (theme == null) {
// set default value
theme = getString(R.string.pref_theme_light_value);
prefs.edit().putString("pref_key_theme", theme).apply();
}
// set theme
UiUtils.setTheme(ctx, theme);
}
private void enableStrictMode() {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
threadPolicy.detectAll();

View File

@@ -92,7 +92,6 @@ public abstract class BriarActivity extends BaseActivity {
window.setEnterTransition(slide);
window.setTransitionBackgroundFadeDuration(getResources()
.getInteger(android.R.integer.config_longAnimTime));
window.setBackgroundDrawableResource(android.R.color.transparent);
}
/**

View File

@@ -11,7 +11,7 @@ import android.text.Spanned;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageButton;
import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
@@ -40,7 +40,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private final ViewGroup layout;
private final AuthorView reblogger;
private final AuthorView author;
private final ImageView reblogButton;
private final ImageButton reblogButton;
private final TextView body;
private final ViewGroup commentContainer;
private final boolean fullText;

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.forum;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
@@ -55,12 +54,8 @@ class ForumListAdapter
ui.postCount.setText(ctx.getResources()
.getQuantityString(R.plurals.posts, postCount,
postCount));
ui.postCount.setTextColor(
ContextCompat.getColor(ctx, R.color.briar_text_secondary));
} else {
ui.postCount.setText(ctx.getString(R.string.no_posts));
ui.postCount.setTextColor(
ContextCompat.getColor(ctx, R.color.briar_text_tertiary));
}
// Date

View File

@@ -16,7 +16,6 @@ import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.view.TextAvatarView;
import static android.support.v4.content.ContextCompat.getColor;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@@ -83,8 +82,6 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
postCount.setText(ctx.getResources()
.getQuantityString(R.plurals.messages, messageCount,
messageCount));
postCount.setTextColor(
getColor(ctx, R.color.briar_text_secondary));
long lastUpdate = group.getTimestamp();
date.setText(UiUtils.formatDate(ctx, lastUpdate));

View File

@@ -33,6 +33,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@@ -84,6 +85,7 @@ public class DevReportActivity extends BaseCrashReportDialog
@Override
public void onCreate(Bundle state) {
getDelegate().setLocalNightMode(MODE_NIGHT_YES);
getDelegate().installViewFactory();
getDelegate().onCreate(state);
super.onCreate(state);
@@ -94,6 +96,7 @@ public class DevReportActivity extends BaseCrashReportDialog
getDelegate().setSupportActionBar(tb);
View requestReport = findViewById(R.id.request_report);
View reportForm = findViewById(R.id.report_form);
userCommentView = findViewById(R.id.user_comment);
userEmailView = findViewById(R.id.user_email);
includeDebugReport = findViewById(R.id.include_debug_report);
@@ -111,13 +114,18 @@ public class DevReportActivity extends BaseCrashReportDialog
if (isFeedback()) {
includeDebugReport
.setText(getString(R.string.include_debug_report_feedback));
reportForm.setVisibility(VISIBLE);
requestReport.setVisibility(INVISIBLE);
} else {
includeDebugReport.setChecked(true);
reportForm.setVisibility(INVISIBLE);
requestReport.setVisibility(VISIBLE);
}
findViewById(R.id.acceptButton).setOnClickListener(v -> {
reviewing = true;
requestReport.setVisibility(GONE);
reportForm.setVisibility(VISIBLE);
requestReport.setVisibility(INVISIBLE);
((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
.showSoftInput(userCommentView, SHOW_FORCED);
});

View File

@@ -34,6 +34,7 @@ import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.Localizer;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.util.UserFeedback;
import java.util.ArrayList;
@@ -44,6 +45,8 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER;
import static android.media.RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI;
import static android.media.RingtoneManager.EXTRA_RINGTONE_EXISTING_URI;
@@ -99,6 +102,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private SettingsActivity listener;
private ListPreference language;
private ListPreference theme;
private ListPreference enableBluetooth;
private ListPreference torNetwork;
private CheckBoxPreference notifyPrivateMessages;
@@ -135,6 +139,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
language = (ListPreference) findPreference(LANGUAGE);
setLanguageEntries();
theme = (ListPreference) findPreference("pref_key_theme");
enableBluetooth = (ListPreference) findPreference("pref_key_bluetooth");
torNetwork = (ListPreference) findPreference("pref_key_tor_network");
notifyPrivateMessages = (CheckBoxPreference) findPreference(
@@ -154,6 +159,23 @@ public class SettingsFragment extends PreferenceFragmentCompat
setSettingsEnabled(false);
language.setOnPreferenceChangeListener(this);
theme.setOnPreferenceChangeListener((preference, newValue) -> {
if (getActivity() != null) {
// activate new theme
UiUtils.setTheme(getActivity(), (String) newValue);
// bring up parent activity, so it can change its theme as well
Intent intent =
new Intent(getActivity(), NavDrawerActivity.class);
intent.setFlags(
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// bring this activity back to the foreground
intent = new Intent(getActivity(), getActivity().getClass());
startActivity(intent);
getActivity().finish();
}
return true;
});
enableBluetooth.setOnPreferenceChangeListener(this);
torNetwork.setOnPreferenceChangeListener(this);
if (SDK_INT >= 21) {
@@ -320,6 +342,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
private void setSettingsEnabled(boolean enabled) {
// theme not needed here, because handled by SharedPreferences
enableBluetooth.setEnabled(enabled);
torNetwork.setEnabled(enabled);
notifyPrivateMessages.setEnabled(enabled);

View File

@@ -4,10 +4,8 @@ import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.CallSuper;
import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
@@ -20,6 +18,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import org.briarproject.briar.android.view.AuthorView;
import static android.support.v4.content.ContextCompat.getColor;
@UiThread
@NotNullByDefault
public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
@@ -61,10 +61,9 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
private void animateFadeOut() {
setIsRecyclable(false);
ValueAnimator anim = new ValueAnimator();
ColorDrawable viewColor = new ColorDrawable(ContextCompat
.getColor(getContext(), R.color.forum_cell_highlight));
anim.setIntValues(viewColor.getColor(), ContextCompat
.getColor(getContext(), R.color.window_background));
int viewColor = getColor(getContext(), R.color.thread_item_highlight);
anim.setIntValues(viewColor,
getColor(getContext(), R.color.window_background));
anim.setEvaluator(new ArgbEvaluator());
anim.setInterpolator(new AccelerateInterpolator());
anim.addListener(new Animator.AnimatorListener() {

View File

@@ -286,6 +286,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
public void onReplyClick(I item) {
replyId = item.getId();
updateTextInput();
// FIXME This does not work for a hardware keyboard
if (textInput.isKeyboardOpen()) {
scrollToItemAtTop(item);
} else {

View File

@@ -38,6 +38,11 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.MANUFACTURER;
import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
import static android.support.v7.app.AppCompatDelegate.setDefaultNightMode;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
@@ -191,4 +196,20 @@ public class UiUtils {
if (v.getFilterTouchesWhenObscured() != filter)
v.setFilterTouchesWhenObscured(!filter);
}
public static void setTheme(Context ctx, String theme) {
if (theme.equals(ctx.getString(R.string.pref_theme_light_value))) {
setDefaultNightMode(MODE_NIGHT_NO);
} else if (theme
.equals(ctx.getString(R.string.pref_theme_dark_value))) {
setDefaultNightMode(MODE_NIGHT_YES);
} else if (theme
.equals(ctx.getString(R.string.pref_theme_auto_value))) {
setDefaultNightMode(MODE_NIGHT_AUTO);
} else if (theme
.equals(ctx.getString(R.string.pref_theme_system_value))) {
setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM);
}
}
}

View File

@@ -7,7 +7,6 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import org.briarproject.briar.R;
@@ -58,7 +57,7 @@ public class LargeTextInputView extends TextInputView {
if (buttonText != null) setButtonText(buttonText);
if (maxLines > 0) ui.editText.setMaxLines(maxLines);
if (fillHeight) {
LinearLayout layout = findViewById(R.id.input_layout);
ViewGroup layout = findViewById(R.id.input_layout);
LayoutParams params = (LayoutParams) layout.getLayoutParams();
params.height = 0;
params.weight = 1;

View File

@@ -38,7 +38,7 @@ public class TrustIndicatorView extends ImageView {
res = R.drawable.trust_indicator_verified;
break;
case OURSELVES:
res = R.drawable.ic_our_identity_black;
res = R.drawable.ic_our_identity;
break;
default:
res = R.drawable.trust_indicator_unknown;

View File

@@ -2,11 +2,11 @@ package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.support.annotation.UiThread;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ImageButton;
import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
import static android.view.MotionEvent.ACTION_CANCEL;
@@ -14,7 +14,7 @@ import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
@UiThread
public class RepeatableImageKey extends ImageButton {
public class RepeatableImageKey extends AppCompatImageButton {
private KeyEventListener listener;

View File

@@ -1,10 +1,13 @@
package org.thoughtcrime.securesms.components.emoji;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -26,6 +29,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.support.v4.widget.ImageViewCompat.setImageTintList;
import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.KEYCODE_DEL;
import static android.widget.ImageView.ScaleType.CENTER_INSIDE;
@@ -142,8 +146,10 @@ public class EmojiDrawer extends LinearLayout {
return pages.size();
}
@NonNull
@Override
public Object instantiateItem(ViewGroup container, int position) {
public Object instantiateItem(@NonNull ViewGroup container,
int position) {
EmojiPageView page = new EmojiPageView(context);
page.setModel(pages.get(position));
page.setEmojiSelectedListener(listener);
@@ -152,21 +158,24 @@ public class EmojiDrawer extends LinearLayout {
}
@Override
public void destroyItem(ViewGroup container, int position,
Object object) {
public void destroyItem(@NonNull ViewGroup container, int position,
@NonNull Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
public boolean isViewFromObject(@NonNull View view,
@NonNull Object object) {
return view == object;
}
@Override
public View getCustomTabView(ViewGroup viewGroup, int i) {
ImageView image = new ImageView(context);
ImageView image = new AppCompatImageView(context);
image.setScaleType(CENTER_INSIDE);
image.setImageResource(pages.get(i).getIcon());
setImageTintList(image, ColorStateList.valueOf(
ContextCompat.getColor(context, R.color.color_primary)));
return image;
}

View File

@@ -4,8 +4,8 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.widget.ImageButton;
import org.briarproject.briar.R;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiDrawerListener;
@@ -13,7 +13,8 @@ import org.thoughtcrime.securesms.components.emoji.EmojiDrawer.EmojiDrawerListen
import javax.annotation.Nullable;
@UiThread
public class EmojiToggle extends ImageButton implements EmojiDrawerListener {
public class EmojiToggle extends AppCompatImageButton
implements EmojiDrawerListener {
private final Drawable emojiToggle;
private final Drawable imeToggle;
@@ -33,7 +34,7 @@ public class EmojiToggle extends ImageButton implements EmojiDrawerListener {
emojiToggle = ContextCompat
.getDrawable(getContext(), R.drawable.ic_emoji_toggle);
imeToggle = ContextCompat
.getDrawable(getContext(), R.drawable.ic_keyboard_black);
.getDrawable(getContext(), R.drawable.ic_keyboard);
setToEmoji();
}