diff --git a/app/build.gradle b/app/build.gradle index ae6dda08..d0f5750e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,15 +95,15 @@ dependencies { implementation('com.mikepenz:materialdrawer:6.1.2@aar') { transitive = true } - implementation 'androidx.core:core:1.0.2' - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core:1.1.0' + implementation 'androidx.appcompat:appcompat:1.1.0' 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-alpha10' implementation 'androidx.exifinterface:exifinterface:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.preference:preference:1.1.0-alpha04' + implementation 'androidx.preference:preference:1.1.0' implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" @@ -123,7 +123,7 @@ dependencies { implementation 'androidx.emoji:emoji-appcompat:1.0.0' implementation 'de.c1710:filemojicompat:1.0.17' // architecture components - implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' //room implementation 'androidx.room:room-runtime:2.1.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' @@ -157,5 +157,5 @@ dependencies { implementation 'com.github.bumptech.glide:okhttp3-integration:4.10.0' //Add some useful extensions - implementation 'androidx.core:core-ktx:1.2.0-alpha01' + implementation 'androidx.core:core-ktx:1.2.0-alpha04' } diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 389a745e..05cc99c2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -52,8 +52,6 @@ import javax.inject.Inject; public abstract class BaseActivity extends AppCompatActivity implements Injectable { - @Inject - public ThemeUtils themeUtils; @Inject public AccountManager accountManager; @@ -75,8 +73,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab setTheme(R.style.TuskyBlackTheme); } - themeUtils.setAppNightMode(theme, this); - /* set the taskdescription programmatically, the theme would turn it blue */ String appName = getString(R.string.app_name); Bitmap appIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt index d1e0c6a8..fc07271a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt @@ -32,7 +32,6 @@ import dagger.android.DispatchingAndroidInjector import kotlinx.android.synthetic.main.toolbar_basic.* import java.lang.IllegalArgumentException import javax.inject.Inject -import androidx.appcompat.app.AppCompatDelegate import dagger.android.HasAndroidInjector class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener, @@ -124,18 +123,11 @@ 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) restartActivitiesOnExit = true this.restartCurrentActivity() - // MODE_NIGHT_FOLLOW_SYSTEM workaround part 2 :/ - when(theme){ - ThemeUtils.THEME_SYSTEM -> { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) - } - } - //workaround end } "statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars" -> { restartActivitiesOnExit = true diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java index 39c21cdc..b135d8d7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java @@ -17,6 +17,7 @@ package com.keylesspalace.tusky; import android.app.Application; import android.content.Context; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.preference.PreferenceManager; @@ -30,6 +31,7 @@ import com.keylesspalace.tusky.di.AppInjector; import com.keylesspalace.tusky.util.EmojiCompatFont; import com.keylesspalace.tusky.util.LocaleManager; import com.keylesspalace.tusky.util.NotificationPullJobCreator; +import com.keylesspalace.tusky.util.ThemeUtils; import com.uber.autodispose.AutoDisposePlugins; import org.conscrypt.Conscrypt; @@ -91,6 +93,7 @@ public class TuskyApplication extends Application implements HasAndroidInjector initAppInjector(); initEmojiCompat(); + initNightMode(); JobManager.create(this).addJobCreator(notificationPullJobCreator); @@ -133,6 +136,12 @@ public class TuskyApplication extends Application implements HasAndroidInjector AppInjector.INSTANCE.init(this); } + protected void initNightMode() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + String theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT); + ThemeUtils.setAppNightMode(theme); + } + public ServiceLocator getServiceLocator() { return serviceLocator; } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 6cbdc7d0..5a082cfe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -324,7 +324,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { } else { inactiveId = ThemeUtils.getDrawableId(reblogButton.getContext(), R.attr.status_reblog_inactive_drawable, R.drawable.reblog_inactive_dark); - activeId = R.drawable.reblog_active; + activeId = R.drawable.ic_reblog_active_24dp; } reblogButton.setInactiveImage(inactiveId); reblogButton.setActiveImage(activeId); 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 93a1b322..9436b7a5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThemeUtils.java @@ -26,29 +26,21 @@ 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 javax.inject.Inject; -import javax.inject.Singleton; - /** * Provides runtime compatibility to obtain theme information and re-theme views, especially where * the ability to do so is not supported in resource files. */ -@Singleton public class ThemeUtils { - @Inject - public ThemeUtils(){} - public static final String APP_THEME_DEFAULT = ThemeUtils.THEME_NIGHT; private static final String THEME_NIGHT = "night"; 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"; + private static final String THEME_SYSTEM = "auto_system"; public static Drawable getDrawable(@NonNull Context context, @AttrRes int attribute, @DrawableRes int fallbackDrawable) { @@ -62,8 +54,9 @@ public class ThemeUtils { return context.getDrawable(resourceId); } - public static @DrawableRes int getDrawableId(@NonNull Context context, @AttrRes int attribute, - @DrawableRes int fallbackDrawableId) { + @DrawableRes + public static int getDrawableId(@NonNull Context context, @AttrRes int attribute, + @DrawableRes int fallbackDrawableId) { TypedValue value = new TypedValue(); if (context.getTheme().resolveAttribute(attribute, value, true)) { return value.resourceId; @@ -72,7 +65,8 @@ public class ThemeUtils { } } - public static @ColorInt int getColor(@NonNull Context context, @AttrRes int attribute) { + @ColorInt + public static int getColor(@NonNull Context context, @AttrRes int attribute) { TypedValue value = new TypedValue(); if (context.getTheme().resolveAttribute(attribute, value, true)) { return value.data; @@ -81,14 +75,16 @@ public class ThemeUtils { } } - public static @ColorRes int getColorId(@NonNull Context context, @AttrRes int attribute) { + @ColorRes + public static int getColorId(@NonNull Context context, @AttrRes int attribute) { TypedValue value = new TypedValue(); context.getTheme().resolveAttribute(attribute, value, true); return value.resourceId; } /** 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) { + @Nullable + public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableId, @AttrRes int colorAttr) { Drawable drawable = context.getDrawable(drawableId); if(drawable == null) { return null; @@ -101,30 +97,21 @@ public class ThemeUtils { drawable.setColorFilter(getColor(context, attribute), PorterDuff.Mode.SRC_IN); } - public void setAppNightMode(String flavor, Context context) { + public static void setAppNightMode(String flavor) { switch (flavor) { default: case THEME_NIGHT: + case THEME_BLACK: AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); break; case THEME_DAY: AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); break; - case THEME_BLACK: - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - break; case THEME_AUTO: - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO); + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_TIME); 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; } } diff --git a/app/src/main/res/drawable/ic_reblog_active_24dp.xml b/app/src/main/res/drawable/ic_reblog_active_24dp.xml new file mode 100644 index 00000000..8d28a400 --- /dev/null +++ b/app/src/main/res/drawable/ic_reblog_active_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_reblog_dark_18dp.xml b/app/src/main/res/drawable/ic_reblog_dark_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_reblog_dark_18dp.xml rename to app/src/main/res/drawable/ic_reblog_dark_24dp.xml diff --git a/app/src/main/res/drawable/ic_reblog_light_18dp.xml b/app/src/main/res/drawable/ic_reblog_light_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_reblog_light_18dp.xml rename to app/src/main/res/drawable/ic_reblog_light_24dp.xml diff --git a/app/src/main/res/drawable/reblog_active.xml b/app/src/main/res/drawable/reblog_active.xml deleted file mode 100644 index d27942e5..00000000 --- a/app/src/main/res/drawable/reblog_active.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/item_status.xml b/app/src/main/res/layout/item_status.xml index 2a9614b9..a0e2b851 100644 --- a/app/src/main/res/layout/item_status.xml +++ b/app/src/main/res/layout/item_status.xml @@ -435,7 +435,7 @@ app:layout_constraintEnd_toStartOf="@id/status_favourite" app:layout_constraintStart_toEndOf="@id/status_reply" app:layout_constraintTop_toTopOf="@id/status_reply" - sparkbutton:activeImage="@drawable/reblog_active" + sparkbutton:activeImage="@drawable/ic_reblog_active_24dp" sparkbutton:iconSize="28dp" sparkbutton:inactiveImage="?attr/status_reblog_inactive_drawable" sparkbutton:primaryColor="@color/tusky_blue" diff --git a/app/src/main/res/layout/item_status_detailed.xml b/app/src/main/res/layout/item_status_detailed.xml index 01db587e..8db4d0d9 100644 --- a/app/src/main/res/layout/item_status_detailed.xml +++ b/app/src/main/res/layout/item_status_detailed.xml @@ -525,7 +525,7 @@ app:layout_constraintEnd_toStartOf="@id/status_favourite" app:layout_constraintStart_toEndOf="@id/status_reply" app:layout_constraintTop_toTopOf="@id/status_reply" - sparkbutton:activeImage="@drawable/reblog_active" + sparkbutton:activeImage="@drawable/ic_reblog_active_24dp" sparkbutton:iconSize="28dp" sparkbutton:inactiveImage="?attr/status_reblog_inactive_drawable" sparkbutton:primaryColor="@color/tusky_blue" diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 8212c850..2733e1e6 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -30,7 +30,7 @@ @color/toolbar_background_dark @color/toolbar_icon_dark @style/TuskyImageButton.Dark - @drawable/ic_reblog_dark_18dp + @drawable/ic_reblog_dark_24dp @drawable/reblog_inactive_dark @drawable/reblog_private_dark @drawable/reblog_direct_dark diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 22353321..919a5417 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -80,7 +80,7 @@ @color/toolbar_background_light @color/toolbar_icon_light @style/TuskyImageButton.Light - @drawable/ic_reblog_light_18dp + @drawable/ic_reblog_light_24dp @drawable/reblog_inactive_light @drawable/reblog_private_light @drawable/reblog_direct_light diff --git a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt index 343ac1e1..bb924ee4 100644 --- a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt @@ -26,14 +26,12 @@ 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 org.junit.Assert import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.robolectric.Robolectric @@ -58,7 +56,6 @@ class ComposeActivityTest { private lateinit var activity: ComposeActivity private lateinit var accountManagerMock: AccountManager private lateinit var apiMock: MastodonApi - private lateinit var themeUtilsMock: ThemeUtils private val account = AccountEntity( id = 1, @@ -85,9 +82,9 @@ class ComposeActivityTest { val controller = Robolectric.buildActivity(ComposeActivity::class.java) activity = controller.get() - accountManagerMock = Mockito.mock(AccountManager::class.java) + accountManagerMock = mock(AccountManager::class.java) - apiMock = Mockito.mock(MastodonApi::class.java) + apiMock = mock(MastodonApi::class.java) `when`(apiMock.getCustomEmojis()).thenReturn(object: Call> { override fun isExecuted(): Boolean { return false @@ -125,12 +122,9 @@ 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) @@ -185,7 +179,7 @@ class ComposeActivityTest { fun whenTextContainsNoUrl_everyCharacterIsCounted() { val content = "This is test content please ignore thx " insertSomeTextInContent(content) - Assert.assertEquals(activity.calculateTextLength(), content.length) + assertEquals(activity.calculateTextLength(), content.length) } @Test @@ -193,7 +187,7 @@ class ComposeActivityTest { val url = "https://www.google.dk/search?biw=1920&bih=990&tbm=isch&sa=1&ei=bmDrWuOoKMv6kwWOkIaoDQ&q=indiana+jones+i+hate+snakes+animated&oq=indiana+jones+i+hate+snakes+animated&gs_l=psy-ab.3...54174.55443.0.55553.9.7.0.0.0.0.255.333.1j0j1.2.0....0...1c.1.64.psy-ab..7.0.0....0.40G-kcDkC6A#imgdii=PSp15hQjN1JqvM:&imgrc=H0hyE2JW5wrpBM:" val additionalContent = "Check out this @image #search result: " insertSomeTextInContent(additionalContent + url) - Assert.assertEquals(activity.calculateTextLength(), additionalContent.length + ComposeActivity.MAXIMUM_URL_LENGTH) + assertEquals(activity.calculateTextLength(), additionalContent.length + ComposeActivity.MAXIMUM_URL_LENGTH) } @Test diff --git a/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt b/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt index c99effea..640f6826 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt @@ -16,6 +16,10 @@ class FakeTuskyApplication : TuskyApplication() { // No-op } + override fun initNightMode() { + // No-op + } + override fun getServiceLocator(): ServiceLocator { return locator } diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt index 70458644..2e627d96 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt @@ -37,7 +37,6 @@ class FilterTest { val activity = controller.get() activity.accountManager = mock() - activity.themeUtils = mock() val apiMock = Mockito.mock(MastodonApi::class.java) Mockito.`when`(apiMock.getFilters()).thenReturn(object: Call> { override fun isExecuted(): Boolean { diff --git a/build.gradle b/build.gradle index 98df4e94..c77e27fa 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta06' - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta07' + classpath 'com.android.tools.build:gradle:3.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } }