├── colors ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── themes.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── marverenic │ │ │ └── colors │ │ │ ├── NightMode.java │ │ │ ├── activity │ │ │ ├── ColorsActivity.java │ │ │ ├── ColorsAppCompatActivity.java │ │ │ └── ColorsActivityDelegate.java │ │ │ ├── ColorTheme.java │ │ │ ├── Colors.java │ │ │ ├── AccentColor.java │ │ │ └── PrimaryColor.java │ └── test │ │ └── java │ │ └── com │ │ └── marverenic │ │ └── colors │ │ ├── activity │ │ └── ColorsActivityDelegateTest.java │ │ └── ColorsTest.java ├── proguard-rules.pro └── build.gradle ├── sample ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── styles.xml │ │ │ └── strings.xml │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── java │ │ └── com │ │ │ └── marverenic │ │ │ └── colors │ │ │ └── demo │ │ │ ├── ColorsDemoApplication.java │ │ │ ├── EnumAdapter.java │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── preview.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── README.md ├── gradlew └── LICENSE /colors/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':colors' 2 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/preview.gif -------------------------------------------------------------------------------- /colors/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Colors 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marverenic/Colors/HEAD/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /colors/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 26 16:36:07 EDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Colors Demo 3 | Primary Color 4 | Accent Color 5 | Night Mode 6 | I\'m feeling lucky 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Android Studio user configurations 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | .DS_Store 6 | /build 7 | *.iml 8 | 9 | # Built application files 10 | *.apk 11 | *.ap_ 12 | 13 | # Files for the Dalvik VM 14 | *.dex 15 | 16 | # Java class files 17 | *.class 18 | 19 | # Generated files 20 | bin/ 21 | gen/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | 30 | # Proguard folder generated by Eclipse 31 | proguard/ 32 | 33 | # Log Files 34 | *.log 35 | -------------------------------------------------------------------------------- /sample/src/main/java/com/marverenic/colors/demo/ColorsDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.demo; 2 | 3 | import android.app.Application; 4 | 5 | import com.marverenic.colors.AccentColor; 6 | import com.marverenic.colors.ColorTheme; 7 | import com.marverenic.colors.Colors; 8 | import com.marverenic.colors.PrimaryColor; 9 | 10 | public class ColorsDemoApplication extends Application { 11 | 12 | @Override 13 | public void onCreate() { 14 | super.onCreate(); 15 | Colors.setTheme(new ColorTheme(PrimaryColor.RED_500, AccentColor.RED_A400)); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.marverenic.demo.colors" 9 | minSdkVersion 11 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.1" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | compile project(':colors') 26 | 27 | testCompile 'junit:junit:4.12' 28 | 29 | compile 'com.android.support:appcompat-v7:25.3.1' 30 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 31 | } 32 | -------------------------------------------------------------------------------- /colors/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\.android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\.android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /sample/src/main/java/com/marverenic/colors/demo/EnumAdapter.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.demo; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.BaseAdapter; 7 | import android.widget.TextView; 8 | 9 | public class EnumAdapter> extends BaseAdapter { 10 | 11 | private E[] mData; 12 | 13 | public EnumAdapter(E[] values) { 14 | mData = values; 15 | } 16 | 17 | @Override 18 | public int getCount() { 19 | return mData.length; 20 | } 21 | 22 | @Override 23 | public E getItem(int position) { 24 | return mData[position]; 25 | } 26 | 27 | @Override 28 | public long getItemId(int position) { 29 | return getItem(position).hashCode(); 30 | } 31 | 32 | @Override 33 | public View getView(int position, View convertView, ViewGroup parent) { 34 | if (convertView == null) { 35 | convertView = LayoutInflater.from(parent.getContext()) 36 | .inflate(android.R.layout.simple_spinner_item, parent, false); 37 | } 38 | 39 | TextView text = (TextView) convertView.findViewById(android.R.id.text1); 40 | text.setText(getItem(position).toString()); 41 | 42 | return convertView; 43 | } 44 | 45 | @Override 46 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 47 | if (convertView == null) { 48 | convertView = LayoutInflater.from(parent.getContext()) 49 | .inflate(android.R.layout.simple_spinner_dropdown_item, parent, false); 50 | } 51 | 52 | TextView text = (TextView) convertView.findViewById(android.R.id.text1); 53 | text.setText(getItem(position).toString()); 54 | 55 | return convertView; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /colors/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.jfrog.bintray' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | 5 | group = 'com.marverenic.colors' 6 | version = '1.1' 7 | 8 | android { 9 | compileSdkVersion 25 10 | buildToolsVersion "25.0.3" 11 | 12 | defaultConfig { 13 | minSdkVersion 11 14 | targetSdkVersion 25 15 | versionCode 2 16 | versionName "1.1" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | compile 'com.android.support:appcompat-v7:25.3.1' 30 | 31 | testCompile 'junit:junit:4.12' 32 | testCompile "com.google.truth:truth:0.33" 33 | testCompile 'org.mockito:mockito-core:2.8.9' 34 | } 35 | 36 | bintray { 37 | user = System.getenv('BINTRAY_USER') 38 | key = System.getenv('BINTRAY_KEY') 39 | 40 | pkg { 41 | repo = 'Colors' 42 | name = 'com.marverenic.colors' 43 | 44 | version { 45 | name = '1.1' 46 | released = new Date() 47 | vcsTag = 'v1.1' 48 | } 49 | 50 | licenses = ['Apache-2.0'] 51 | vcsUrl = 'https://github.com/marverenic/Colors' 52 | websiteUrl = 'https://github.com/marverenic/Colors' 53 | } 54 | configurations = ['archives'] 55 | } 56 | task generateSourcesJar(type: Jar) { 57 | from android.sourceSets.main.java.srcDirs 58 | classifier 'sources' 59 | } 60 | 61 | task javadoc(type: Javadoc) { 62 | source = android.sourceSets.main.java.srcDirs 63 | destinationDir = file("../javadoc/") 64 | failOnError false 65 | } 66 | 67 | task generateJavadocsJar(type: Jar) { 68 | from javadoc.destinationDir 69 | classifier 'javadoc' 70 | dependsOn javadoc 71 | } 72 | 73 | artifacts { 74 | archives generateJavadocsJar 75 | archives generateSourcesJar 76 | } 77 | -------------------------------------------------------------------------------- /colors/src/test/java/com/marverenic/colors/activity/ColorsActivityDelegateTest.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.activity; 2 | 3 | import android.app.Activity; 4 | import android.content.res.Resources; 5 | 6 | import com.marverenic.colors.AccentColor; 7 | import com.marverenic.colors.Colors; 8 | import com.marverenic.colors.PrimaryColor; 9 | 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import static org.mockito.Mockito.mock; 14 | import static org.mockito.Mockito.never; 15 | import static org.mockito.Mockito.verify; 16 | import static org.mockito.Mockito.when; 17 | 18 | public class ColorsActivityDelegateTest { 19 | 20 | private Activity mActivity; 21 | private ColorsActivityDelegate mDelegate; 22 | 23 | @Before 24 | public void setUp() { 25 | mActivity = mock(Activity.class); 26 | when(mActivity.getTheme()).thenReturn(mock(Resources.Theme.class)); 27 | 28 | mDelegate = new ColorsActivityDelegate(mActivity); 29 | } 30 | 31 | @Test 32 | public void testThemeChangesRecreateActivity() { 33 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A100); 34 | mDelegate.onCreate(); 35 | 36 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A200); 37 | mDelegate.onResume(); 38 | 39 | verify(mActivity).recreate(); 40 | } 41 | 42 | @Test 43 | public void testActivityNotRecreatedWithNoThemeChange() { 44 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A100); 45 | mDelegate.onCreate(); 46 | mDelegate.onResume(); 47 | 48 | verify(mActivity, never()).recreate(); 49 | } 50 | 51 | @Test 52 | public void testActivityNotRecreatedWithIntermediateThemeChange() { 53 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A100); 54 | mDelegate.onCreate(); 55 | 56 | Colors.setTheme(PrimaryColor.AMBER_500, AccentColor.BLUE_A100); 57 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A100); 58 | mDelegate.onResume(); 59 | 60 | verify(mActivity, never()).recreate(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /colors/src/main/java/com/marverenic/colors/NightMode.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.support.annotation.IntDef; 6 | import android.support.v7.app.AppCompatDelegate; 7 | 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | 11 | public enum NightMode implements Parcelable { 12 | 13 | DAY(AppCompatDelegate.MODE_NIGHT_NO), 14 | NIGHT(AppCompatDelegate.MODE_NIGHT_YES), 15 | AUTO(AppCompatDelegate.MODE_NIGHT_AUTO), 16 | FOLLOW_SYSTEM(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); 17 | 18 | @NightModeValue 19 | private int mAppcompatMode; 20 | 21 | NightMode(@NightModeValue int value) { 22 | mAppcompatMode = value; 23 | } 24 | 25 | @NightModeValue 26 | int getAppcompatMode() { 27 | return mAppcompatMode; 28 | } 29 | 30 | static NightMode getFromAppcompatMode(@NightModeValue int value) { 31 | for (NightMode nightMode : values()) { 32 | if (nightMode.getAppcompatMode() == value) { 33 | return nightMode; 34 | } 35 | } 36 | 37 | throw new IllegalArgumentException("Invalid appcompat mode: " + value); 38 | } 39 | 40 | @Override 41 | public void writeToParcel(Parcel dest, int flags) { 42 | dest.writeInt(mAppcompatMode); 43 | } 44 | 45 | @Override 46 | public int describeContents() { 47 | return 0; 48 | } 49 | 50 | @IntDef({AppCompatDelegate.MODE_NIGHT_NO, AppCompatDelegate.MODE_NIGHT_YES, 51 | AppCompatDelegate.MODE_NIGHT_AUTO, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM}) 52 | @Retention(RetentionPolicy.SOURCE) 53 | private @interface NightModeValue {} 54 | 55 | public static final Creator CREATOR = new Creator() { 56 | @Override 57 | public NightMode createFromParcel(Parcel in) { 58 | //noinspection WrongConstant 59 | return getFromAppcompatMode(in.readInt()); 60 | } 61 | 62 | @Override 63 | public NightMode[] newArray(int size) { 64 | return new NightMode[size]; 65 | } 66 | }; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /colors/src/main/java/com/marverenic/colors/activity/ColorsActivity.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.activity; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager.TaskDescription; 5 | import android.graphics.Bitmap; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.AppCompatActivity; 9 | 10 | import com.marverenic.colors.AccentColor; 11 | import com.marverenic.colors.Colors; 12 | import com.marverenic.colors.NightMode; 13 | import com.marverenic.colors.PrimaryColor; 14 | 15 | /** 16 | * An extension of {@link Activity} that automatically themes itself. You must call 17 | * {@link Colors#setTheme(PrimaryColor, AccentColor, NightMode)} or one of its overloads before 18 | * creating this Activity. 19 | * 20 | * @see ColorsAppCompatActivity to extend from {@link AppCompatActivity} instead. This is required 21 | * to enable night mode. 22 | * @see ColorsActivityDelegate 23 | */ 24 | public class ColorsActivity extends Activity { 25 | 26 | private ColorsActivityDelegate mDelegate; 27 | 28 | @Override 29 | public void onCreate(@Nullable Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | mDelegate = new ColorsActivityDelegate(this, getTaskName(), getTaskIcon()); 32 | mDelegate.onCreate(); 33 | } 34 | 35 | @Override 36 | protected void onResume() { 37 | super.onResume(); 38 | mDelegate.onResume(); 39 | } 40 | 41 | /** 42 | * Override this method to customize this Activity's {@link TaskDescription}. 43 | * Only used on API 21+. 44 | * @return The name to show for this app on the recent apps page, or {@code null} to use the 45 | * default task name 46 | */ 47 | @Nullable 48 | protected String getTaskName() { 49 | return null; 50 | } 51 | 52 | /** 53 | * Override this method to customize this Activity's {@link TaskDescription}. 54 | * Only used on API 21+. 55 | * @return The icon to show for this app on the recent apps page, or {@code null} to use the 56 | * default task icon 57 | */ 58 | @Nullable 59 | protected Bitmap getTaskIcon() { 60 | return null; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /colors/src/main/java/com/marverenic/colors/activity/ColorsAppCompatActivity.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.activity; 2 | 3 | import android.app.ActivityManager.TaskDescription; 4 | import android.graphics.Bitmap; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | 9 | import com.marverenic.colors.AccentColor; 10 | import com.marverenic.colors.Colors; 11 | import com.marverenic.colors.NightMode; 12 | import com.marverenic.colors.PrimaryColor; 13 | 14 | /** 15 | * An extension of {@link AppCompatActivity} that automatically themes itself. You must call 16 | * {@link Colors#setTheme(PrimaryColor, AccentColor, NightMode)} or one of its overloads before 17 | * creating this Activity. 18 | * 19 | * @see ColorsActivity to extend from {@link android.app.Activity} instead. Not that night mode 20 | * is only supported in AppCompatActivities with a theme that extends from 21 | * {@code Theme.AppCompat.DayNight} or one of its children. 22 | * @see ColorsActivityDelegate 23 | */ 24 | public class ColorsAppCompatActivity extends AppCompatActivity { 25 | 26 | private ColorsActivityDelegate mDelegate; 27 | 28 | @Override 29 | public void onCreate(@Nullable Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | mDelegate = new ColorsActivityDelegate(this, getTaskName(), getTaskIcon()); 32 | mDelegate.onCreate(); 33 | } 34 | 35 | @Override 36 | protected void onResume() { 37 | super.onResume(); 38 | mDelegate.onResume(); 39 | } 40 | 41 | /** 42 | * Override this method to customize this Activity's {@link TaskDescription}. 43 | * Only used on API 21+. 44 | * @return The name to show for this app on the recent apps page, or {@code null} to use the 45 | * default task name 46 | */ 47 | @Nullable 48 | protected String getTaskName() { 49 | return null; 50 | } 51 | 52 | /** 53 | * Override this method to customize this Activity's {@link TaskDescription}. 54 | * Only used on API 21+. 55 | * @return The icon to show for this app on the recent apps page, or {@code null} to use the 56 | * default task icon 57 | */ 58 | @Nullable 59 | protected Bitmap getTaskIcon() { 60 | return null; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /colors/src/test/java/com/marverenic/colors/ColorsTest.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors; 2 | 3 | import android.app.Activity; 4 | import android.content.res.Resources; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.mockito.InOrder; 9 | 10 | import static com.google.common.truth.Truth.assertThat; 11 | import static org.mockito.Mockito.inOrder; 12 | import static org.mockito.Mockito.mock; 13 | import static org.mockito.Mockito.when; 14 | 15 | public class ColorsTest { 16 | 17 | private Activity mActivity; 18 | private Resources.Theme mTheme; 19 | 20 | @Before 21 | public void setUp() { 22 | mActivity = mock(Activity.class); 23 | mTheme = mock(Resources.Theme.class); 24 | 25 | when(mActivity.getTheme()).thenReturn(mTheme); 26 | } 27 | 28 | @Test 29 | public void testThemeAppliesStyles() { 30 | Colors.setTheme(PrimaryColor.INDIGO_500, AccentColor.PINK_A400); 31 | 32 | assertThat(Colors.getTheme().getPrimaryColor()).isEqualTo(PrimaryColor.INDIGO_500); 33 | assertThat(Colors.getTheme().getAccentColor()).isEqualTo(AccentColor.PINK_A400); 34 | 35 | Colors.theme(mActivity); 36 | 37 | InOrder inOrder = inOrder(mTheme); 38 | inOrder.verify(mTheme).applyStyle(R.style._ColorsTheme_Primary_Indigo_500, true); 39 | inOrder.verify(mTheme).applyStyle(R.style._ColorsTheme_Accent_Pink_A400, true); 40 | inOrder.verifyNoMoreInteractions(); 41 | } 42 | 43 | @Test 44 | public void testSetThemeCreatesCorrectThemeWithDefaultNightMode() { 45 | Colors.setTheme(PrimaryColor.BLUE_700, AccentColor.TEAL_A400); 46 | ColorTheme expected = new ColorTheme(PrimaryColor.BLUE_700, AccentColor.TEAL_A400, NightMode.DAY); 47 | 48 | assertThat(Colors.getTheme()).isEqualTo(expected); 49 | } 50 | 51 | @Test 52 | public void testSetThemeCreatesCorrectTheme() { 53 | Colors.setTheme(PrimaryColor.CYAN_500, AccentColor.GREEN_A100, NightMode.AUTO); 54 | ColorTheme expected = new ColorTheme(PrimaryColor.CYAN_500, AccentColor.GREEN_A100, NightMode.AUTO); 55 | 56 | assertThat(Colors.getTheme()).isEqualTo(expected); 57 | } 58 | 59 | @Test(expected = IllegalArgumentException.class) 60 | public void testSetThemeThrowsForNullPrimaryColor() { 61 | //noinspection ConstantConditions 62 | Colors.setTheme(null, AccentColor.AMBER_A200); 63 | } 64 | 65 | @Test(expected = IllegalArgumentException.class) 66 | public void testSetThemeThrowsForNullAccentColor() { 67 | //noinspection ConstantConditions 68 | Colors.setTheme(PrimaryColor.RED_500, null); 69 | } 70 | 71 | @Test(expected = IllegalArgumentException.class) 72 | public void testSetThemeThrowsForNullNightMode() { 73 | //noinspection ConstantConditions 74 | Colors.setTheme(PrimaryColor.RED_500, AccentColor.AMBER_A100, null); 75 | } 76 | 77 | @Test(expected = IllegalArgumentException.class) 78 | public void testSetThemeThrowsForNullTheme() { 79 | //noinspection ConstantConditions 80 | Colors.setTheme(null); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /colors/src/main/java/com/marverenic/colors/activity/ColorsActivityDelegate.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors.activity; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager.TaskDescription; 5 | import android.graphics.Bitmap; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | 9 | import com.marverenic.colors.ColorTheme; 10 | import com.marverenic.colors.Colors; 11 | 12 | /** 13 | * Serves as a delegate for the theming logic for an activity. This class takes care of theming 14 | * activities and triggering activity recreations when the theme changes when an activity is paused. 15 | * 16 | * To use this class, simply create an instance of it in one of your activities, and call the 17 | * corresponding methods in this class in the appropriate lifecycle callback (e.g. In your 18 | * {@link Activity#onResume()}, call {@link #onResume()}). Delegates should be kept until the 19 | * activity is destroyed, but do not need to be persisted across activity recreations or 20 | * configuration changes. 21 | * 22 | * @see ColorsActivity for an extension of Activity that implements this behavior 23 | * @see ColorsAppCompatActivity for an extension of AppCompatActvity that implements this behavior 24 | */ 25 | public final class ColorsActivityDelegate { 26 | 27 | private Activity mActivity; 28 | private ColorTheme mTheme; 29 | private String mTaskName; 30 | private Bitmap mTaskIcon; 31 | 32 | /** 33 | * Creates a new ColorsActivityDelegate for an Activity 34 | * @param parent The activity that will be delegating to this object 35 | * @see #ColorsActivityDelegate(Activity, String, Bitmap) to customize the Activity's 36 | * {@link TaskDescription} 37 | */ 38 | public ColorsActivityDelegate(Activity parent) { 39 | this(parent, null, null); 40 | } 41 | 42 | /** 43 | * Creates a new ColorsActivityDelegate for an Activity 44 | * @param parent The activity that will be delegating to this object 45 | * @param taskName The name that should show up in this app's overview entry, or {@code null} 46 | * for the default value 47 | * @param taskIcon The icon that should appear in this app's overview entry, or {@code null} 48 | * for the default value 49 | * @see #ColorsActivityDelegate(Activity, String, Bitmap) to customize the Activity's 50 | * {@link TaskDescription} 51 | */ 52 | public ColorsActivityDelegate(Activity parent, @Nullable String taskName, 53 | @Nullable Bitmap taskIcon) { 54 | mActivity = parent; 55 | mTaskName = taskName; 56 | mTaskIcon = taskIcon; 57 | } 58 | 59 | /** 60 | * Call this in your Activity's {@link Activity#onCreate(Bundle)} method. Note that this MUST 61 | * be called before inflating any views or calling {@link Activity#setContentView(int)}. 62 | */ 63 | public void onCreate() { 64 | mTheme = Colors.getTheme(); 65 | Colors.theme(mActivity, mTaskName, mTaskIcon); 66 | } 67 | 68 | /** 69 | * Call this in your Activity's {@link Activity#onResume()} method. 70 | */ 71 | public void onResume() { 72 | if (!Colors.getTheme().equals(mTheme)) { 73 | mActivity.recreate(); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /colors/src/main/java/com/marverenic/colors/ColorTheme.java: -------------------------------------------------------------------------------- 1 | package com.marverenic.colors; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import android.support.annotation.NonNull; 6 | 7 | public final class ColorTheme implements Parcelable { 8 | 9 | @NonNull 10 | private final PrimaryColor mPrimaryColor; 11 | 12 | @NonNull 13 | private final AccentColor mAccentColor; 14 | 15 | @NonNull 16 | private final NightMode mNightMode; 17 | 18 | public ColorTheme(@NonNull PrimaryColor primaryColor, @NonNull AccentColor accentColor) { 19 | this(primaryColor, accentColor, NightMode.DAY); 20 | } 21 | 22 | public ColorTheme(@NonNull PrimaryColor primaryColor, @NonNull AccentColor accentColor, 23 | @NonNull NightMode nightMode) { 24 | if (primaryColor == null) { 25 | throw new IllegalArgumentException("primary color cannot be null"); 26 | } else if (accentColor == null) { 27 | throw new IllegalArgumentException("accent color cannot be null"); 28 | } else if (nightMode == null) { 29 | throw new IllegalArgumentException("night mode cannot be null"); 30 | } 31 | 32 | mPrimaryColor = primaryColor; 33 | mAccentColor = accentColor; 34 | mNightMode = nightMode; 35 | } 36 | 37 | private ColorTheme(Parcel in) { 38 | this( 39 | (PrimaryColor) in.readParcelable(PrimaryColor.class.getClassLoader()), 40 | (AccentColor) in.readParcelable(AccentColor.class.getClassLoader()), 41 | (NightMode) in.readParcelable(NightMode.class.getClassLoader()) 42 | ); 43 | } 44 | 45 | @NonNull 46 | public PrimaryColor getPrimaryColor() { 47 | return mPrimaryColor; 48 | } 49 | 50 | @NonNull 51 | public AccentColor getAccentColor() { 52 | return mAccentColor; 53 | } 54 | 55 | @NonNull 56 | public NightMode getNightMode() { 57 | return mNightMode; 58 | } 59 | 60 | @Override 61 | public int describeContents() { 62 | return 0; 63 | } 64 | 65 | @Override 66 | public void writeToParcel(Parcel dest, int flags) { 67 | dest.writeParcelable(mPrimaryColor, 0); 68 | dest.writeParcelable(mAccentColor, 0); 69 | dest.writeParcelable(mNightMode, 0); 70 | } 71 | 72 | public static final Creator CREATOR = new Creator() { 73 | @Override 74 | public ColorTheme createFromParcel(Parcel in) { 75 | return new ColorTheme(in); 76 | } 77 | 78 | @Override 79 | public ColorTheme[] newArray(int size) { 80 | return new ColorTheme[size]; 81 | } 82 | }; 83 | 84 | @Override 85 | public boolean equals(Object o) { 86 | if (this == o) return true; 87 | if (o == null || getClass() != o.getClass()) return false; 88 | 89 | ColorTheme that = (ColorTheme) o; 90 | 91 | return this.mPrimaryColor == that.mPrimaryColor 92 | && this.mAccentColor == that.mAccentColor 93 | && this.mNightMode == that.mNightMode; 94 | 95 | } 96 | 97 | @Override 98 | public int hashCode() { 99 | int result = mPrimaryColor.hashCode(); 100 | result = 31 * result + mAccentColor.hashCode(); 101 | result = 31 * result + mNightMode.hashCode(); 102 | return result; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Colors (Deprecated) 2 | 3 | ⚠ **Colors is deprecated** as of January 26, 2020. 4 | This library will continue to work, but will not receive support or updates. 5 | Consider using [garretyoder/Colorful](https://github.com/garretyoder/Colorful) instead. 6 | [Read more](https://andrewbailey.dev/post/2020/01/26/rebranding-marverenic). 7 | 8 | --- 9 | 10 | [ ![Download](https://api.bintray.com/packages/marverenic/Colors/com.marverenic.colors/images/download.svg)](https://bintray.com/marverenic/Colors/com.marverenic.colors/_latestVersion) 11 | [![CircleCI](https://circleci.com/gh/marverenic/Colors.svg?style=svg)](https://circleci.com/gh/marverenic/Colors) 12 | 13 | Colors is a theming library for Android. It allows your app to use over 5,000 color combinations from the [Material Design Palette](https://material.io/guidelines/style/color.html#color-color-tool) by separately picking a primary and accent color. 14 | 15 |

16 | 17 |

18 | 19 | ## Gradle Installation 20 | ``` 21 | dependencies { 22 | compile 'com.marverenic.colors:colors:1.x' 23 | } 24 | ``` 25 | 26 | The latest version and release notes can be found on the [Releases](https://github.com/marverenic/Colors/releases) page. 27 | 28 | ## Usage 29 | ### Set a Theme 30 | To use Colors, first set a global application theme. You need to do this before you show any themed Activities. A good place to set a default theme is in your `Application` class. You can set your theme with a single line of code: 31 | 32 | ```java 33 | Colors.setTheme(PrimaryColor.INDIGO_500, AccentColor.PINK_A200); 34 | ``` 35 | 36 | ### Theming an Activity 37 | Colors only sets the primary and accent color of the theme, so build your activity as you normally would and give it a basic theme. To make Colors theme your activity, simply make your activity extend from either `ColorsActivity` or `ColorsAppCompatActivity`. 38 | 39 | Colors will recreate your activity if the theme was changed when your activity is returned to. To immediately apply a theme, make sure you call `recreate()` in your Activity after calling `Colors.setTheme(...)`. 40 | 41 | #### ColorsActivityDelegate 42 | Already extending from another library's Activity? No problem. Just create an instance of `ColorsActivityDelegate` in your Activity and call its `onCreate` and `onResume` methods as shown: 43 | 44 | ```java 45 | public class YourActivity extends Activity { 46 | private ColorsActivityDelegate delegate = new ColorsActivityDelegate(this); 47 | 48 | @Override 49 | public void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | delegate.onCreate(); // Must be called before setContentView 52 | 53 | setContentView(/* Your layout resource */); 54 | // More initialization code for your Activity 55 | } 56 | 57 | @Override 58 | public void onResume() { 59 | super.onResume(); 60 | delegate.onResume(); 61 | } 62 | } 63 | ``` 64 | 65 | ### User Theme Preferences 66 | Colors doesn't provide a built-in way to offer the user a theme choice. Developers can build any interface to present to the user and add any number of constraints to which themes can be built. 67 | 68 | Colors also does not save the last chosen theme automatically. If you're using `SharedPreferences`, however, you can easily save the theme: 69 | 70 | ```java 71 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 72 | PrimaryColor primaryColor = PrimaryColor.INDIGO_500; 73 | AccentColor accentColor = AccentColor.PINK_A200; 74 | 75 | prefs.edit() 76 | .putString(PRIMARY_COLOR_PREF_KEY, primaryColor.getId()) 77 | .putString(ACCENT_COLOR_PREF_KEY, accentColor.getId()) 78 | .apply(); 79 | ``` 80 | 81 | And restore it just as easily: 82 | 83 | ```java 84 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 85 | PrimaryColor defaultPrimary = PrimaryColor.INDIGO_500; 86 | AccentColor defaultAccent = AccentColor.PINK_A200; 87 | 88 | PrimaryColor primaryColor = PrimaryColor.findById( 89 | prefs.getString(PRIMARY_COLOR_PREF_KEY, defaultPrimary.getId())); 90 | 91 | AccentColor accentColor = AccentColor.findById( 92 | prefs.getString(ACCENT_COLOR_PREF_KEY, defaultAccent.getId())); 93 | 94 | Colors.setTheme(primaryColor, accentColor); 95 | ``` 96 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 22 | 23 | 35 | 36 | 49 | 50 | 62 | 63 | 76 | 77 | 89 | 90 |