From 507ffb1b41bd937ebcb23e661977c23421e91532 Mon Sep 17 00:00:00 2001 From: Bernd Date: Thu, 7 Mar 2019 21:33:29 +0100 Subject: [PATCH] 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 --- app/build.gradle | 6 +-- .../com/keylesspalace/tusky/BaseActivity.java | 5 ++- .../tusky/PreferencesActivity.kt | 11 +++++- .../com/keylesspalace/tusky/SplashActivity.kt | 10 +---- .../conversation/ConversationViewHolder.java | 8 ++-- .../keylesspalace/tusky/util/ThemeUtils.java | 38 +++++++++---------- app/src/main/res/values/donottranslate.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../tusky/ComposeActivityTest.kt | 6 +++ 9 files changed, 45 insertions(+), 41 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8d1b9ceb2..47147d455 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ dependencies { implementation 'androidx.browser:browser:1.0.0' implementation 'androidx.recyclerview:recyclerview: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.cardview:cardview:1.0.0' implementation 'androidx.preference:preference:1.1.0-alpha03' @@ -117,13 +117,12 @@ dependencies { kapt 'androidx.room:room-compiler:2.0.0' implementation 'androidx.room:room-rxjava2:2.0.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' implementation "com.google.dagger:dagger:$daggerVersion" kapt "com.google.dagger:dagger-compiler:$daggerVersion" implementation "com.google.dagger:dagger-android:$daggerVersion" implementation "com.google.dagger:dagger-android-support:$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 "com.nhaarman.mockitokotlin2:mockito-kotlin:2.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 "androidx.test.ext:junit:1.1.0" + testImplementation "androidx.test.ext:junit:1.1.0" debugImplementation 'im.dino:dbinspector:3.4.1@aar' implementation 'io.reactivex.rxjava2:rxjava:2.2.6' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 57a46f875..c1d4cbc1e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -56,6 +56,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab @Inject public AccountManager accountManager; + ThemeUtils themeUtils = new ThemeUtils(); + protected static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1; @Override @@ -72,7 +74,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab if (theme.equals("black")) { setTheme(R.style.TuskyBlackTheme); } - ThemeUtils.setAppNightMode(theme, this); + + themeUtils.setAppNightMode(theme, this); /* set the taskdescription programmatically, the theme would turn it blue */ String appName = getString(R.string.app_name); diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt index dac4869e5..305de956b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt @@ -34,6 +34,7 @@ import dagger.android.support.HasSupportFragmentInjector import kotlinx.android.synthetic.main.toolbar_basic.* import java.lang.IllegalArgumentException import javax.inject.Inject +import androidx.appcompat.app.AppCompatDelegate class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener, HasSupportFragmentInjector { @@ -123,7 +124,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference "appTheme" -> { val theme = sharedPreferences.getNonNullString("appTheme", ThemeUtils.APP_THEME_DEFAULT) Log.d("activeTheme", theme) - ThemeUtils.setAppNightMode(theme, this) + ThemeUtils().setAppNightMode(theme, this) restartActivitiesOnExit = true // recreate() could be used instead, but it doesn't have an animation B). @@ -135,7 +136,13 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference finish() 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" -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/SplashActivity.kt b/app/src/main/java/com/keylesspalace/tusky/SplashActivity.kt index 8c2974b84..57959955e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/SplashActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/SplashActivity.kt @@ -17,18 +17,10 @@ package com.keylesspalace.tusky import android.content.Intent 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 javax.inject.Inject - -class SplashActivity : AppCompatActivity(), Injectable { - - @Inject - lateinit var accountManager: AccountManager +class SplashActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java index 44bc89f56..8a72075be 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java @@ -106,14 +106,12 @@ public class ConversationViewHolder extends StatusBaseViewHolder { private void setConversationName(List accounts) { Context context = conversationNameTextView.getContext(); - String conversationName; - if(accounts.size() == 0) { - conversationName = " "; - }else if(accounts.size() == 1) { + String conversationName = ""; + if(accounts.size() == 1) { conversationName = context.getString(R.string.conversation_1_recipients, accounts.get(0).getUsername()); } else if(accounts.size() == 2) { 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); } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java index 2ba900e7d..c079bb27d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java @@ -15,12 +15,10 @@ package com.keylesspalace.tusky.util; -import android.app.UiModeManager; import android.content.Context; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; -import android.os.Build; import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; @@ -28,8 +26,8 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatDelegate; +import android.provider.Settings; import android.util.TypedValue; -import android.widget.ImageView; /** * 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_BLACK = "black"; 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, @DrawableRes int fallbackDrawable) { @@ -85,10 +84,6 @@ public class ThemeUtils { 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 */ public static @Nullable Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) { Drawable drawable = context.getDrawable(drawableId); @@ -103,30 +98,31 @@ public class ThemeUtils { drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN); } - public static void setAppNightMode(String flavor, Context context) { - int mode; + public void setAppNightMode(String flavor, Context context) { switch (flavor) { default: case THEME_NIGHT: - mode = UiModeManager.MODE_NIGHT_YES; + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); break; case THEME_DAY: - mode = UiModeManager.MODE_NIGHT_NO; + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); break; case THEME_BLACK: - mode = UiModeManager.MODE_NIGHT_YES; + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); break; 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; } - - 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); - } - } } diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 1584d5d0b..97847dca5 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -28,6 +28,7 @@ day black auto + auto_system diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 48b04f8ee..2e74e1849 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -205,6 +205,7 @@ Light Black Automatic at sunset + Use System Design Browser diff --git a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt index 76411d8d8..754dc238b 100644 --- a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt @@ -26,6 +26,7 @@ import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Instance import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.util.ThemeUtils import okhttp3.Request import okhttp3.ResponseBody import org.junit.Assert @@ -44,6 +45,7 @@ import retrofit2.Call import retrofit2.Callback import retrofit2.Response + /** * Created by charlag on 3/7/18. */ @@ -55,6 +57,7 @@ class ComposeActivityTest { lateinit var activity: ComposeActivity lateinit var accountManagerMock: AccountManager lateinit var apiMock: MastodonApi + lateinit var themeUtilsMock: ThemeUtils val account = AccountEntity( id = 1, @@ -135,9 +138,12 @@ class ComposeActivityTest { val dbMock = mock(AppDatabase::class.java) `when`(dbMock.instanceDao()).thenReturn(instanceDaoMock) + themeUtilsMock = Mockito.mock(ThemeUtils::class.java) + activity.mastodonApi = apiMock activity.accountManager = accountManagerMock activity.database = dbMock + activity.themeUtils = themeUtilsMock `when`(accountManagerMock.activeAccount).thenReturn(account)