Add new Theme "Use System Design" + fixes to night mode (#1069)
* Add theme system A theme which follows system design. See: https://www.xda-developers.com/samsung-galaxy-s9-update-night-mode-schedule/ * update to be in line with https://github.com/tuskyapp/Tusky/pull/1060/files * Update ThemeUtils.java * update * Cleanup * Update Deps * Cleanup * Update PreferencesActivity.kt * Workaround to make MODE_NIGHT_FOLLOW_SYSTEM work * Update ThemeUtils.java * Use ThemeUtils.THEME_SYSTEM * Update SplashActivity.kt * Update strings.xml * Update Deps * Update build.gradle * Update build.gradle * fix tests
This commit is contained in:
parent
006300ede6
commit
507ffb1b41
9 changed files with 45 additions and 41 deletions
|
@ -86,7 +86,7 @@ dependencies {
|
||||||
implementation 'androidx.browser:browser:1.0.0'
|
implementation 'androidx.browser:browser:1.0.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||||
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0-alpha03'
|
implementation 'com.google.android.material:material:1.1.0-alpha04'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.preference:preference:1.1.0-alpha03'
|
implementation 'androidx.preference:preference:1.1.0-alpha03'
|
||||||
|
@ -117,13 +117,12 @@ dependencies {
|
||||||
kapt 'androidx.room:room-compiler:2.0.0'
|
kapt 'androidx.room:room-compiler:2.0.0'
|
||||||
implementation 'androidx.room:room-rxjava2:2.0.0'
|
implementation 'androidx.room:room-rxjava2:2.0.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
testImplementation 'junit:junit:4.12'
|
|
||||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||||
implementation "com.google.dagger:dagger-android:$daggerVersion"
|
implementation "com.google.dagger:dagger-android:$daggerVersion"
|
||||||
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
|
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
|
||||||
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
|
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
|
||||||
testImplementation 'org.robolectric:robolectric:4.1'
|
testImplementation 'org.robolectric:robolectric:4.2'
|
||||||
testImplementation 'org.mockito:mockito-inline:2.24.0'
|
testImplementation 'org.mockito:mockito-inline:2.24.0'
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
|
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0"
|
||||||
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
|
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
|
||||||
|
@ -131,6 +130,7 @@ dependencies {
|
||||||
})
|
})
|
||||||
androidTestImplementation('android.arch.persistence.room:testing:1.1.1')
|
androidTestImplementation('android.arch.persistence.room:testing:1.1.1')
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.0"
|
androidTestImplementation "androidx.test.ext:junit:1.1.0"
|
||||||
|
testImplementation "androidx.test.ext:junit:1.1.0"
|
||||||
debugImplementation 'im.dino:dbinspector:3.4.1@aar'
|
debugImplementation 'im.dino:dbinspector:3.4.1@aar'
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
|
||||||
|
|
|
@ -56,6 +56,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
@Inject
|
@Inject
|
||||||
public AccountManager accountManager;
|
public AccountManager accountManager;
|
||||||
|
|
||||||
|
ThemeUtils themeUtils = new ThemeUtils();
|
||||||
|
|
||||||
protected static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
|
protected static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,7 +74,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
if (theme.equals("black")) {
|
if (theme.equals("black")) {
|
||||||
setTheme(R.style.TuskyBlackTheme);
|
setTheme(R.style.TuskyBlackTheme);
|
||||||
}
|
}
|
||||||
ThemeUtils.setAppNightMode(theme, this);
|
|
||||||
|
themeUtils.setAppNightMode(theme, this);
|
||||||
|
|
||||||
/* set the taskdescription programmatically, the theme would turn it blue */
|
/* set the taskdescription programmatically, the theme would turn it blue */
|
||||||
String appName = getString(R.string.app_name);
|
String appName = getString(R.string.app_name);
|
||||||
|
|
|
@ -34,6 +34,7 @@ import dagger.android.support.HasSupportFragmentInjector
|
||||||
import kotlinx.android.synthetic.main.toolbar_basic.*
|
import kotlinx.android.synthetic.main.toolbar_basic.*
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
|
||||||
class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener, HasSupportFragmentInjector {
|
class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener, HasSupportFragmentInjector {
|
||||||
|
|
||||||
|
@ -123,7 +124,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
||||||
"appTheme" -> {
|
"appTheme" -> {
|
||||||
val theme = sharedPreferences.getNonNullString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
|
val theme = sharedPreferences.getNonNullString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
|
||||||
Log.d("activeTheme", theme)
|
Log.d("activeTheme", theme)
|
||||||
ThemeUtils.setAppNightMode(theme, this)
|
ThemeUtils().setAppNightMode(theme, this)
|
||||||
restartActivitiesOnExit = true
|
restartActivitiesOnExit = true
|
||||||
|
|
||||||
// recreate() could be used instead, but it doesn't have an animation B).
|
// recreate() could be used instead, but it doesn't have an animation B).
|
||||||
|
@ -135,7 +136,13 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
||||||
finish()
|
finish()
|
||||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
|
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
|
||||||
|
|
||||||
restartActivitiesOnExit = true
|
// MODE_NIGHT_FOLLOW_SYSTEM workaround part 2 :/
|
||||||
|
when(theme){
|
||||||
|
ThemeUtils.THEME_SYSTEM -> {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//workaround end
|
||||||
|
|
||||||
}
|
}
|
||||||
"statusTextSize" -> {
|
"statusTextSize" -> {
|
||||||
|
|
|
@ -17,18 +17,10 @@ package com.keylesspalace.tusky
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
|
|
||||||
import com.keylesspalace.tusky.db.AccountManager
|
|
||||||
import com.keylesspalace.tusky.di.Injectable
|
|
||||||
import com.keylesspalace.tusky.util.NotificationHelper
|
import com.keylesspalace.tusky.util.NotificationHelper
|
||||||
|
|
||||||
import javax.inject.Inject
|
class SplashActivity : BaseActivity() {
|
||||||
|
|
||||||
class SplashActivity : AppCompatActivity(), Injectable {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var accountManager: AccountManager
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
|
@ -106,14 +106,12 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
||||||
|
|
||||||
private void setConversationName(List<ConversationAccountEntity> accounts) {
|
private void setConversationName(List<ConversationAccountEntity> accounts) {
|
||||||
Context context = conversationNameTextView.getContext();
|
Context context = conversationNameTextView.getContext();
|
||||||
String conversationName;
|
String conversationName = "";
|
||||||
if(accounts.size() == 0) {
|
if(accounts.size() == 1) {
|
||||||
conversationName = " ";
|
|
||||||
}else if(accounts.size() == 1) {
|
|
||||||
conversationName = context.getString(R.string.conversation_1_recipients, accounts.get(0).getUsername());
|
conversationName = context.getString(R.string.conversation_1_recipients, accounts.get(0).getUsername());
|
||||||
} else if(accounts.size() == 2) {
|
} else if(accounts.size() == 2) {
|
||||||
conversationName = context.getString(R.string.conversation_2_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername());
|
conversationName = context.getString(R.string.conversation_2_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername());
|
||||||
} else {
|
} else if (accounts.size() > 2){
|
||||||
conversationName = context.getString(R.string.conversation_more_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername(), accounts.size() - 2);
|
conversationName = context.getString(R.string.conversation_more_recipients, accounts.get(0).getUsername(), accounts.get(1).getUsername(), accounts.size() - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,10 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.util;
|
package com.keylesspalace.tusky.util;
|
||||||
|
|
||||||
import android.app.UiModeManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.annotation.AttrRes;
|
import androidx.annotation.AttrRes;
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.ColorRes;
|
import androidx.annotation.ColorRes;
|
||||||
|
@ -28,8 +26,8 @@ import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides runtime compatibility to obtain theme information and re-theme views, especially where
|
* Provides runtime compatibility to obtain theme information and re-theme views, especially where
|
||||||
|
@ -42,6 +40,7 @@ public class ThemeUtils {
|
||||||
private static final String THEME_DAY = "day";
|
private static final String THEME_DAY = "day";
|
||||||
private static final String THEME_BLACK = "black";
|
private static final String THEME_BLACK = "black";
|
||||||
private static final String THEME_AUTO = "auto";
|
private static final String THEME_AUTO = "auto";
|
||||||
|
public static final String THEME_SYSTEM = "auto_system";
|
||||||
|
|
||||||
public static Drawable getDrawable(@NonNull Context context, @AttrRes int attribute,
|
public static Drawable getDrawable(@NonNull Context context, @AttrRes int attribute,
|
||||||
@DrawableRes int fallbackDrawable) {
|
@DrawableRes int fallbackDrawable) {
|
||||||
|
@ -85,10 +84,6 @@ public class ThemeUtils {
|
||||||
ResourcesUtils.getResourceIdentifier(context, "attr", name));
|
ResourcesUtils.getResourceIdentifier(context, "attr", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setImageViewTint(ImageView view, @AttrRes int attribute) {
|
|
||||||
view.setColorFilter(getColor(view.getContext(), attribute), PorterDuff.Mode.SRC_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** this can be replaced with drawableTint in xml once minSdkVersion >= 23 */
|
/** this can be replaced with drawableTint in xml once minSdkVersion >= 23 */
|
||||||
public static @Nullable Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) {
|
public static @Nullable Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) {
|
||||||
Drawable drawable = context.getDrawable(drawableId);
|
Drawable drawable = context.getDrawable(drawableId);
|
||||||
|
@ -103,30 +98,31 @@ public class ThemeUtils {
|
||||||
drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN);
|
drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setAppNightMode(String flavor, Context context) {
|
public void setAppNightMode(String flavor, Context context) {
|
||||||
int mode;
|
|
||||||
switch (flavor) {
|
switch (flavor) {
|
||||||
default:
|
default:
|
||||||
case THEME_NIGHT:
|
case THEME_NIGHT:
|
||||||
mode = UiModeManager.MODE_NIGHT_YES;
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
break;
|
break;
|
||||||
case THEME_DAY:
|
case THEME_DAY:
|
||||||
mode = UiModeManager.MODE_NIGHT_NO;
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
break;
|
break;
|
||||||
case THEME_BLACK:
|
case THEME_BLACK:
|
||||||
mode = UiModeManager.MODE_NIGHT_YES;
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
break;
|
break;
|
||||||
case THEME_AUTO:
|
case THEME_AUTO:
|
||||||
mode = UiModeManager.MODE_NIGHT_AUTO;
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
|
||||||
|
break;
|
||||||
|
case THEME_SYSTEM:
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||||
|
|
||||||
|
//stupid workaround to make MODE_NIGHT_FOLLOW_SYSTEM work :(
|
||||||
|
if((Settings.System.getInt(context.getContentResolver(), "display_night_theme", 0) == 1)) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||||
|
} else if ((Settings.System.getInt(context.getContentResolver(), "display_night_theme", 0) == 0)) {
|
||||||
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
UiModeManager uiModeManager = (UiModeManager)context.getApplicationContext().getSystemService(Context.UI_MODE_SERVICE);
|
|
||||||
uiModeManager.setNightMode(mode);
|
|
||||||
} else {
|
|
||||||
AppCompatDelegate.setDefaultNightMode(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
<item>day</item>
|
<item>day</item>
|
||||||
<item>black</item>
|
<item>black</item>
|
||||||
<item>auto</item>
|
<item>auto</item>
|
||||||
|
<item>auto_system</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -205,6 +205,7 @@
|
||||||
<item>Light</item>
|
<item>Light</item>
|
||||||
<item>Black</item>
|
<item>Black</item>
|
||||||
<item>Automatic at sunset</item>
|
<item>Automatic at sunset</item>
|
||||||
|
<item>Use System Design</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string name="pref_title_browser_settings">Browser</string>
|
<string name="pref_title_browser_settings">Browser</string>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.entity.Emoji
|
import com.keylesspalace.tusky.entity.Emoji
|
||||||
import com.keylesspalace.tusky.entity.Instance
|
import com.keylesspalace.tusky.entity.Instance
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.util.ThemeUtils
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
|
@ -44,6 +45,7 @@ import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 3/7/18.
|
* Created by charlag on 3/7/18.
|
||||||
*/
|
*/
|
||||||
|
@ -55,6 +57,7 @@ class ComposeActivityTest {
|
||||||
lateinit var activity: ComposeActivity
|
lateinit var activity: ComposeActivity
|
||||||
lateinit var accountManagerMock: AccountManager
|
lateinit var accountManagerMock: AccountManager
|
||||||
lateinit var apiMock: MastodonApi
|
lateinit var apiMock: MastodonApi
|
||||||
|
lateinit var themeUtilsMock: ThemeUtils
|
||||||
|
|
||||||
val account = AccountEntity(
|
val account = AccountEntity(
|
||||||
id = 1,
|
id = 1,
|
||||||
|
@ -135,9 +138,12 @@ class ComposeActivityTest {
|
||||||
val dbMock = mock(AppDatabase::class.java)
|
val dbMock = mock(AppDatabase::class.java)
|
||||||
`when`(dbMock.instanceDao()).thenReturn(instanceDaoMock)
|
`when`(dbMock.instanceDao()).thenReturn(instanceDaoMock)
|
||||||
|
|
||||||
|
themeUtilsMock = Mockito.mock(ThemeUtils::class.java)
|
||||||
|
|
||||||
activity.mastodonApi = apiMock
|
activity.mastodonApi = apiMock
|
||||||
activity.accountManager = accountManagerMock
|
activity.accountManager = accountManagerMock
|
||||||
activity.database = dbMock
|
activity.database = dbMock
|
||||||
|
activity.themeUtils = themeUtilsMock
|
||||||
|
|
||||||
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue