diff --git a/.gitignore b/.gitignore index 1adb7743..11d21dc0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ local.properties /app/release/ /*.hprof /.idea/deploymentTargetDropDown.xml +/.kotlin/sessions/*.salive diff --git a/README.md b/README.md index 1430282b..d5f952af 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ # ColorBlendr -### An application to modify material you colors of your device. +### Customize Material You colors of your device.

Elevate your creativity with effortless material customization. Instantly tweak colors for a personalized touch in just a few taps. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5506e664..180573ff 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { defaultConfig { minSdk = 31 targetSdk = 34 - versionCode = 12 - versionName = "v1.4" + versionCode = 13 + versionName = "v1.5" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d58e18f..919fda6e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,11 +70,20 @@ + + + + + android:label="RestartServiceWhenStopped" + tools:ignore="ExportedReceiver" /> EXCLUDED_PREFS_FROM_BACKUP = new HashSet<>( Arrays.asList( + FIRST_RUN, PREF_WORKING_METHOD, - MONET_LAST_UPDATED + MONET_LAST_UPDATED, + THEMING_ENABLED, + SHIZUKU_THEMING_ENABLED, + WALLPAPER_COLOR_LIST ) ); diff --git a/app/src/main/java/com/drdisagree/colorblendr/config/RPrefs.java b/app/src/main/java/com/drdisagree/colorblendr/config/RPrefs.java index c47bce37..97bcd17a 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/config/RPrefs.java +++ b/app/src/main/java/com/drdisagree/colorblendr/config/RPrefs.java @@ -2,6 +2,7 @@ import static android.content.Context.MODE_PRIVATE; import static com.drdisagree.colorblendr.common.Const.EXCLUDED_PREFS_FROM_BACKUP; +import static com.drdisagree.colorblendr.common.Const.THEMING_ENABLED; import android.content.SharedPreferences; import android.util.Log; @@ -133,6 +134,10 @@ public static void restorePrefs(final @NonNull InputStream inputStream) { // Retrieve excluded prefs from current prefs Map excludedPrefs = new HashMap<>(); + + // Restoring config will enable theming service + excludedPrefs.put(THEMING_ENABLED, true); + for (String excludedPref : EXCLUDED_PREFS_FROM_BACKUP) { Object prefValue = prefs.getAll().get(excludedPref); if (prefValue != null) { diff --git a/app/src/main/java/com/drdisagree/colorblendr/extension/ThemeOverlayPackage.java b/app/src/main/java/com/drdisagree/colorblendr/extension/ThemeOverlayPackage.java index ffb2e069..c0c9a752 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/extension/ThemeOverlayPackage.java +++ b/app/src/main/java/com/drdisagree/colorblendr/extension/ThemeOverlayPackage.java @@ -41,7 +41,7 @@ public static JSONObject getThemeCustomizationOverlayPackages() { ColorUtil.intToHexColorNoHash(RPrefs.getInt(MONET_SEED_COLOR, Color.BLUE)) ); } - object.putOpt(APPLIED_TIMESTAMP, String.valueOf(System.currentTimeMillis())); + object.putOpt(APPLIED_TIMESTAMP, System.currentTimeMillis()); } catch (Exception e) { Log.e(TAG, "getThemeCustomizationOverlayPackages:", e); } diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/BackgroundService.java b/app/src/main/java/com/drdisagree/colorblendr/service/AutoStartService.java similarity index 70% rename from app/src/main/java/com/drdisagree/colorblendr/service/BackgroundService.java rename to app/src/main/java/com/drdisagree/colorblendr/service/AutoStartService.java index 7d099aef..e7aa0b58 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/service/BackgroundService.java +++ b/app/src/main/java/com/drdisagree/colorblendr/service/AutoStartService.java @@ -1,5 +1,6 @@ package com.drdisagree.colorblendr.service; +import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; @@ -15,7 +16,9 @@ import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.drdisagree.colorblendr.BuildConfig; import com.drdisagree.colorblendr.ColorBlendr; import com.drdisagree.colorblendr.R; import com.drdisagree.colorblendr.common.Const; @@ -26,20 +29,36 @@ import com.drdisagree.colorblendr.utils.ShizukuUtil; import com.drdisagree.colorblendr.utils.SystemUtil; -public class BackgroundService extends Service { +import java.util.Timer; +import java.util.TimerTask; - private static final String TAG = BackgroundService.class.getSimpleName(); +public class AutoStartService extends Service { + + private static final String TAG = AutoStartService.class.getSimpleName(); private static boolean isRunning = false; private static final int NOTIFICATION_ID = 1; private static final String NOTIFICATION_CHANNEL_ID = "Background Service"; private static BroadcastListener myReceiver; private NotificationManager notificationManager; - public BackgroundService() { + // for testing background running service + private final boolean TEST_BACKGROUND_SERVICE = false; + private final boolean debugging = BuildConfig.DEBUG && TEST_BACKGROUND_SERVICE; + public int counter = 0; + private Timer timer; + private static final String packageName = ColorBlendr.getAppContext().getPackageName(); + public static final String ACTION_FOO = packageName + ".FOO"; + public static final String EXTRA_PARAM_A = packageName + ".PARAM_A"; + + public AutoStartService() { isRunning = false; myReceiver = new BroadcastListener(); } + public static boolean isServiceNotRunning() { + return !isRunning; + } + @Nullable @Override public IBinder onBind(Intent intent) { @@ -51,11 +70,12 @@ public void onCreate() { super.onCreate(); if (notificationManager == null) { - notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager = getSystemService(NotificationManager.class); } isRunning = true; createNotificationChannel(); + showNotification(); registerReceivers(); if (BroadcastListener.lastOrientation == -1) { @@ -65,12 +85,50 @@ public void onCreate() { @Override public int onStartCommand(Intent intent, int flags, int startId) { - showNotification(); + super.onStartCommand(intent, flags, startId); + + if (debugging) { + // for testing background running service + startTimer(this); + } + setupSystemUIRestartListener(); return START_STICKY; } + @Override + public void onDestroy() { + super.onDestroy(); + + isRunning = false; + Log.i(TAG, "onDestroy: Service is destroyed :("); + + try { + unregisterReceiver(myReceiver); + } catch (Exception ignored) { + // Receiver was probably never registered + } + + Intent broadcastIntent = new Intent(this, RestartBroadcastReceiver.class); + sendBroadcast(broadcastIntent); + + if (debugging) { + // for testing background running service + stopTimerTask(); + } + } + + private void createNotificationChannel() { + NotificationChannel channel = new NotificationChannel( + NOTIFICATION_CHANNEL_ID, + getString(R.string.background_service_notification_channel_title), + NotificationManager.IMPORTANCE_DEFAULT + ); + channel.setDescription(getString(R.string.background_service_notification_channel_text)); + notificationManager.createNotificationChannel(channel); + } + private void showNotification() { Intent notificationIntent = new Intent(); notificationIntent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS); @@ -85,7 +143,7 @@ private void showNotification() { PendingIntent.FLAG_IMMUTABLE ); - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder( + Notification notification = new NotificationCompat.Builder( this, NOTIFICATION_CHANNEL_ID ) @@ -95,12 +153,13 @@ private void showNotification() { .setContentText(getString(R.string.background_service_notification_text)) .setContentIntent(pendingIntent) .setSound(null, AudioManager.STREAM_NOTIFICATION) - .setColor(ColorUtil.getAccentColor(this)); + .setColor(ColorUtil.getAccentColor(this)) + .build(); - notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); + startForeground(NOTIFICATION_ID, notification); } - @SuppressWarnings("deprecation") + @SuppressWarnings("all") private void registerReceivers() { IntentFilter intentFilterWithoutScheme = new IntentFilter(); intentFilterWithoutScheme.addAction(Intent.ACTION_WALLPAPER_CHANGED); @@ -121,16 +180,6 @@ private void registerReceivers() { registerReceiver(myReceiver, intentFilterWithScheme); } - public void createNotificationChannel() { - NotificationChannel channel = new NotificationChannel( - NOTIFICATION_CHANNEL_ID, - getString(R.string.background_service_notification_channel_title), - NotificationManager.IMPORTANCE_LOW - ); - channel.setDescription(getString(R.string.background_service_notification_channel_text)); - notificationManager.createNotificationChannel(channel); - } - private void setupSystemUIRestartListener() { if (Const.getWorkingMethod() == Const.WORK_METHOD.ROOT && RootConnectionProvider.isNotConnected() @@ -139,7 +188,7 @@ private void setupSystemUIRestartListener() { .runOnSuccess(new MethodInterface() { @Override public void run() { - setupSysUIRestartListener(); + initSystemUIRestartListener(); } }) .run(); @@ -153,11 +202,11 @@ public void run() { ShizukuConnectionProvider.serviceConnection ); } else if (Const.getWorkingMethod() == Const.WORK_METHOD.ROOT) { - setupSysUIRestartListener(); + initSystemUIRestartListener(); } } - private void setupSysUIRestartListener() { + private void initSystemUIRestartListener() { try { ColorBlendr.getRootConnection().setSystemUIRestartListener(); } catch (RemoteException e) { @@ -165,22 +214,28 @@ private void setupSysUIRestartListener() { } } - public static boolean isServiceNotRunning() { - return !isRunning; + public void startTimer(Context context) { + timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + Log.i(TAG, "Timer is running " + counter++); + broadcastActionTest(context, String.valueOf(counter)); + } + }, 1000, 1000); } - @Override - public void onDestroy() { - isRunning = false; - stopForeground(true); - try { - unregisterReceiver(myReceiver); - } catch (Exception ignored) { - // Receiver was probably never registered - } - Intent broadcastIntent = new Intent(this, ServiceDestroyedListener.class); - sendBroadcast(broadcastIntent); + public static void broadcastActionTest(Context context, String param) { + Intent intent = new Intent(ACTION_FOO); + intent.putExtra(EXTRA_PARAM_A, param); + LocalBroadcastManager bm = LocalBroadcastManager.getInstance(context); + bm.sendBroadcast(intent); + } - super.onDestroy(); + public void stopTimerTask() { + if (timer != null) { + timer.cancel(); + timer = null; + } } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/BroadcastListener.java b/app/src/main/java/com/drdisagree/colorblendr/service/BroadcastListener.java index bc05b52a..2977e420 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/service/BroadcastListener.java +++ b/app/src/main/java/com/drdisagree/colorblendr/service/BroadcastListener.java @@ -18,7 +18,6 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import com.drdisagree.colorblendr.ColorBlendr; import com.drdisagree.colorblendr.common.Const; import com.drdisagree.colorblendr.config.RPrefs; import com.drdisagree.colorblendr.extension.MethodInterface; @@ -26,7 +25,7 @@ import com.drdisagree.colorblendr.utils.AppUtil; import com.drdisagree.colorblendr.utils.OverlayManager; import com.drdisagree.colorblendr.utils.SystemUtil; -import com.drdisagree.colorblendr.utils.WallpaperUtil; +import com.drdisagree.colorblendr.utils.WallpaperColorUtil; import java.util.ArrayList; import java.util.HashMap; @@ -52,10 +51,8 @@ public void onReceive(Context context, Intent intent) { Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction()) ) { // Start background service on boot - if (AppUtil.permissionsGranted(context)) { - if (BackgroundService.isServiceNotRunning()) { - context.startService(new Intent(ColorBlendr.getAppContext(), BackgroundService.class)); - } + if (AppUtil.permissionsGranted(context) && AutoStartService.isServiceNotRunning()) { + context.startForegroundService(new Intent(context, AutoStartService.class)); } validateRootAndUpdateColors(context, new MethodInterface() { @@ -72,7 +69,7 @@ public void run() { if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction()) && AppUtil.permissionsGranted(context) ) { - ArrayList wallpaperColors = WallpaperUtil.getWallpaperColors(context); + ArrayList wallpaperColors = WallpaperColorUtil.getWallpaperColors(context); RPrefs.putString(WALLPAPER_COLOR_LIST, Const.GSON.toJson(wallpaperColors)); if (!RPrefs.getBoolean(MONET_SEED_COLOR_ENABLED, false)) { diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/RestartBroadcastReceiver.java b/app/src/main/java/com/drdisagree/colorblendr/service/RestartBroadcastReceiver.java new file mode 100644 index 00000000..a3481f3d --- /dev/null +++ b/app/src/main/java/com/drdisagree/colorblendr/service/RestartBroadcastReceiver.java @@ -0,0 +1,37 @@ +package com.drdisagree.colorblendr.service; + +import static android.content.Context.JOB_SCHEDULER_SERVICE; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class RestartBroadcastReceiver extends BroadcastReceiver { + + private static final String TAG = RestartBroadcastReceiver.class.getSimpleName(); + private static JobScheduler jobScheduler; + + @Override + public void onReceive(Context context, Intent intent) { + Log.i(TAG, "Service Stopped, but this is a never ending service."); + scheduleJob(context); + } + + public static void scheduleJob(Context context) { + if (jobScheduler == null) { + jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE); + } + + ComponentName componentName = new ComponentName(context, ScheduledJobService.class); + + JobInfo jobInfo = new JobInfo.Builder(1, componentName) + .setOverrideDeadline(0) + .setPersisted(true).build(); + + jobScheduler.schedule(jobInfo); + } +} diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/RestartServiceWorker.java b/app/src/main/java/com/drdisagree/colorblendr/service/RestartServiceWorker.java deleted file mode 100644 index 0427dcc5..00000000 --- a/app/src/main/java/com/drdisagree/colorblendr/service/RestartServiceWorker.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.drdisagree.colorblendr.service; - -import android.content.Context; -import android.content.Intent; - -import androidx.annotation.NonNull; -import androidx.work.Worker; -import androidx.work.WorkerParameters; - -public class RestartServiceWorker extends Worker { - - private final Context context; - - public RestartServiceWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - this.context = context; - } - - @NonNull - @Override - public Result doWork() { - if (BackgroundService.isServiceNotRunning()) { - context.startService(new Intent(context, BackgroundService.class)); - } - - return Result.success(); - } -} diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/ScheduledJobService.java b/app/src/main/java/com/drdisagree/colorblendr/service/ScheduledJobService.java new file mode 100644 index 00000000..1223ce1e --- /dev/null +++ b/app/src/main/java/com/drdisagree/colorblendr/service/ScheduledJobService.java @@ -0,0 +1,36 @@ +package com.drdisagree.colorblendr.service; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Intent; +import android.util.Log; + +import androidx.work.Configuration; + +public class ScheduledJobService extends JobService { + + private static final String TAG = ScheduledJobService.class.getSimpleName(); + + ScheduledJobService() { + @SuppressWarnings("all") Configuration.Builder builder = new Configuration.Builder(); + builder.setJobSchedulerJobIdRange(0, 1000); + } + + @Override + public boolean onStartJob(JobParameters jobParameters) { + ServiceLauncher serviceLauncher = new ServiceLauncher(); + serviceLauncher.launchService(this); + + return false; + } + + @Override + public boolean onStopJob(JobParameters jobParameters) { + Log.i(TAG, "Stopping job..."); + + Intent broadcastIntent = new Intent(getApplicationContext().getPackageName()); + sendBroadcast(broadcastIntent); + + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/ServiceDestroyedListener.java b/app/src/main/java/com/drdisagree/colorblendr/service/ServiceDestroyedListener.java deleted file mode 100644 index 39b55505..00000000 --- a/app/src/main/java/com/drdisagree/colorblendr/service/ServiceDestroyedListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.drdisagree.colorblendr.service; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -public class ServiceDestroyedListener extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - WorkManager workManager = WorkManager.getInstance(context); - workManager.enqueue(new OneTimeWorkRequest.Builder(RestartServiceWorker.class).build()); - } -} diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/ServiceLauncher.java b/app/src/main/java/com/drdisagree/colorblendr/service/ServiceLauncher.java new file mode 100644 index 00000000..d6fde725 --- /dev/null +++ b/app/src/main/java/com/drdisagree/colorblendr/service/ServiceLauncher.java @@ -0,0 +1,30 @@ +package com.drdisagree.colorblendr.service; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class ServiceLauncher { + + private static final String TAG = ServiceLauncher.class.getSimpleName(); + private static Intent serviceIntent = null; + + private void setServiceIntent(Context context) { + if (serviceIntent == null) { + serviceIntent = new Intent(context, AutoStartService.class); + } + } + + public void launchService(Context context) { + if (context == null) { + return; + } + + setServiceIntent(context); + + if (AutoStartService.isServiceNotRunning()) { + Log.d(TAG, "launchService: Service is starting..."); + context.startForegroundService(serviceIntent); + } + } +} diff --git a/app/src/main/java/com/drdisagree/colorblendr/service/ShizukuConnection.java b/app/src/main/java/com/drdisagree/colorblendr/service/ShizukuConnection.java index ebab210f..1e068afc 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/service/ShizukuConnection.java +++ b/app/src/main/java/com/drdisagree/colorblendr/service/ShizukuConnection.java @@ -7,10 +7,12 @@ import android.util.Log; import androidx.annotation.Keep; +import androidx.annotation.NonNull; import com.drdisagree.colorblendr.extension.ThemeOverlayPackage; import com.topjohnwu.superuser.Shell; +import org.json.JSONException; import org.json.JSONObject; public class ShizukuConnection extends IShizukuConnection.Stub { @@ -45,28 +47,7 @@ public void applyFabricatedColors(String jsonString) { @Override public void removeFabricatedColors() { try { - String currentSettings = getCurrentSettings(); - JSONObject jsonObject = new JSONObject(currentSettings); - - String[] keysToRemove = new String[]{ - ThemeOverlayPackage.THEME_STYLE, - ThemeOverlayPackage.COLOR_SOURCE, - ThemeOverlayPackage.SYSTEM_PALETTE - }; - - for (String key : keysToRemove) { - jsonObject.remove(key); - } - - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { - jsonObject.remove(ThemeOverlayPackage.ACCENT_COLOR); - } - - jsonObject.putOpt(ThemeOverlayPackage.COLOR_BOTH, "0"); - jsonObject.putOpt(ThemeOverlayPackage.COLOR_SOURCE, "home_wallpaper"); - jsonObject.putOpt(ThemeOverlayPackage.APPLIED_TIMESTAMP, String.valueOf(System.currentTimeMillis())); - - applyFabricatedColors(jsonObject.toString()); + applyFabricatedColors(getOriginalSettings().toString()); } catch (Exception e) { Log.e(TAG, "removeFabricatedColors: ", e); } @@ -77,4 +58,29 @@ public String getCurrentSettings() { final String mCommand = "settings get secure " + THEME_CUSTOMIZATION_OVERLAY_PACKAGES; return Shell.cmd(mCommand).exec().getOut().get(0); } + + private @NonNull JSONObject getOriginalSettings() throws JSONException { + String currentSettings = getCurrentSettings(); + JSONObject jsonObject = new JSONObject(currentSettings); + + String[] keysToRemove = new String[]{ + ThemeOverlayPackage.THEME_STYLE, + ThemeOverlayPackage.COLOR_SOURCE, + ThemeOverlayPackage.SYSTEM_PALETTE + }; + + for (String key : keysToRemove) { + jsonObject.remove(key); + } + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + jsonObject.remove(ThemeOverlayPackage.ACCENT_COLOR); + } + + jsonObject.putOpt(ThemeOverlayPackage.COLOR_BOTH, "0"); + jsonObject.putOpt(ThemeOverlayPackage.COLOR_SOURCE, "home_wallpaper"); + jsonObject.putOpt(ThemeOverlayPackage.APPLIED_TIMESTAMP, System.currentTimeMillis()); + + return jsonObject; + } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/activities/MainActivity.java b/app/src/main/java/com/drdisagree/colorblendr/ui/activities/MainActivity.java index 2c27c36d..cd8c835a 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/activities/MainActivity.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/activities/MainActivity.java @@ -22,6 +22,7 @@ import com.drdisagree.colorblendr.common.Const; import com.drdisagree.colorblendr.config.RPrefs; import com.drdisagree.colorblendr.databinding.ActivityMainBinding; +import com.drdisagree.colorblendr.service.RestartBroadcastReceiver; import com.drdisagree.colorblendr.ui.fragments.HomeFragment; import com.drdisagree.colorblendr.ui.fragments.OnboardingFragment; import com.google.android.material.appbar.AppBarLayout; @@ -120,4 +121,10 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { } return super.onOptionsItemSelected(item); } + + @Override + protected void onResume() { + super.onResume(); + RestartBroadcastReceiver.scheduleJob(getApplicationContext()); + } } \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/activities/SplashActivity.java b/app/src/main/java/com/drdisagree/colorblendr/ui/activities/SplashActivity.java index f49bc128..4ba6560e 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/activities/SplashActivity.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/activities/SplashActivity.java @@ -18,7 +18,7 @@ import com.drdisagree.colorblendr.service.ShizukuConnection; import com.drdisagree.colorblendr.utils.FabricatedUtil; import com.drdisagree.colorblendr.utils.ShizukuUtil; -import com.drdisagree.colorblendr.utils.WallpaperUtil; +import com.drdisagree.colorblendr.utils.WallpaperColorUtil; import com.google.android.material.color.DynamicColors; import java.util.concurrent.CountDownLatch; @@ -40,7 +40,7 @@ public class SplashActivity extends AppCompatActivity { .runOnSuccess(new MethodInterface() { @Override public void run() { - WallpaperUtil.getAndSaveWallpaperColors(getApplicationContext()); + WallpaperColorUtil.getAndSaveWallpaperColors(getApplicationContext()); FabricatedUtil.getAndSaveSelectedFabricatedApps(getApplicationContext()); success.set(true); keepShowing = false; diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorPaletteFragment.java b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorPaletteFragment.java index bf5fcdbe..86d00be1 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorPaletteFragment.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorPaletteFragment.java @@ -199,6 +199,18 @@ private void enablePaletteOnClickListener(LinearLayout[] colorTableRows) { return; } + if (finalJ == 0 || finalJ == 12) { + Snackbar.make( + requireView(), + getString(R.string.cannot_override_color), + Snackbar.LENGTH_SHORT + ) + .setAction(getString(R.string.dismiss), v2 -> { + }) + .show(); + return; + } + new ColorPickerDialog() .withCornerRadius(10) .withColor((Integer) v.getTag()) diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorsFragment.java b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorsFragment.java index 3e1cb67b..73ecf383 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorsFragment.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/ColorsFragment.java @@ -33,7 +33,7 @@ import com.drdisagree.colorblendr.utils.ColorUtil; import com.drdisagree.colorblendr.utils.MiscUtil; import com.drdisagree.colorblendr.utils.OverlayManager; -import com.drdisagree.colorblendr.utils.WallpaperUtil; +import com.drdisagree.colorblendr.utils.WallpaperColorUtil; import com.google.gson.reflect.TypeToken; import java.util.ArrayList; @@ -81,7 +81,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, monetSeedColor = new int[]{RPrefs.getInt( MONET_SEED_COLOR, - WallpaperUtil.getWallpaperColor(requireContext()) + WallpaperColorUtil.getWallpaperColor(requireContext()) )}; return binding.getRoot(); diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/HomeFragment.java b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/HomeFragment.java index 83a9b568..9a1e7f47 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/HomeFragment.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/HomeFragment.java @@ -20,7 +20,7 @@ import com.drdisagree.colorblendr.ColorBlendr; import com.drdisagree.colorblendr.R; import com.drdisagree.colorblendr.databinding.FragmentHomeBinding; -import com.drdisagree.colorblendr.service.BackgroundService; +import com.drdisagree.colorblendr.service.AutoStartService; import com.drdisagree.colorblendr.utils.AppUtil; import com.drdisagree.colorblendr.utils.FragmentUtil; import com.google.android.material.snackbar.Snackbar; @@ -55,8 +55,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat new Handler(Looper.getMainLooper()).postDelayed(() -> { try { if (AppUtil.permissionsGranted(requireContext())) { - if (BackgroundService.isServiceNotRunning()) { - requireContext().startService(new Intent(ColorBlendr.getAppContext(), BackgroundService.class)); + if (AutoStartService.isServiceNotRunning()) { + requireContext().startForegroundService(new Intent(requireContext(), AutoStartService.class)); } } else { requestPermissionsLauncher.launch(AppUtil.REQUIRED_PERMISSIONS); @@ -166,8 +166,8 @@ private void handlePermissionsResult(Map result) { return; } - if (BackgroundService.isServiceNotRunning()) { - requireContext().startService(new Intent(ColorBlendr.getAppContext(), BackgroundService.class)); + if (AutoStartService.isServiceNotRunning()) { + requireContext().startForegroundService(new Intent(requireContext(), AutoStartService.class)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/OnboardingFragment.java b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/OnboardingFragment.java index 6ca66de5..3096ed0d 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/OnboardingFragment.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/OnboardingFragment.java @@ -30,7 +30,7 @@ import com.drdisagree.colorblendr.utils.AppUtil; import com.drdisagree.colorblendr.utils.FabricatedUtil; import com.drdisagree.colorblendr.utils.ShizukuUtil; -import com.drdisagree.colorblendr.utils.WallpaperUtil; +import com.drdisagree.colorblendr.utils.WallpaperColorUtil; import java.util.ArrayList; @@ -130,7 +130,7 @@ private void checkShizukuConnection() { private void goToHomeFragment() { Const.saveWorkingMethod(Const.WORKING_METHOD); - WallpaperUtil.getAndSaveWallpaperColors(requireContext()); + WallpaperColorUtil.getAndSaveWallpaperColors(requireContext()); FabricatedUtil.getAndSaveSelectedFabricatedApps(requireContext()); RPrefs.putBoolean(FIRST_RUN, false); MainActivity.replaceFragment(new HomeFragment(), true); @@ -140,44 +140,58 @@ private void animateBackButton(int position) { int duration = 300; if (position == 0 && binding.btnPrev.getVisibility() == View.VISIBLE) { - AlphaAnimation fadeOut = new AlphaAnimation(1.0f, 0.0f); - fadeOut.setDuration(duration); - fadeOut.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - } + AlphaAnimation fadeOut = getFadeOutAnimation(duration); + binding.btnPrev.startAnimation(fadeOut); + } else if (position != 0 && binding.btnPrev.getVisibility() != View.VISIBLE) { + AlphaAnimation fadeIn = getFadeInAnimation(duration); + binding.btnPrev.startAnimation(fadeIn); + } + } - @Override - public void onAnimationEnd(Animation animation) { - binding.btnPrev.setVisibility(View.GONE); - } + private @NonNull AlphaAnimation getFadeOutAnimation(int duration) { + AlphaAnimation fadeOut = new AlphaAnimation(1.0f, 0.0f); - @Override - public void onAnimationRepeat(Animation animation) { - } - }); + fadeOut.setDuration(duration); - binding.btnPrev.startAnimation(fadeOut); - } else if (position != 0 && binding.btnPrev.getVisibility() != View.VISIBLE) { - AlphaAnimation fadeIn = new AlphaAnimation(0.0f, 1.0f); - fadeIn.setDuration(duration); - fadeIn.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - binding.btnPrev.setVisibility(View.VISIBLE); - } + fadeOut.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } - @Override - public void onAnimationEnd(Animation animation) { - } + @Override + public void onAnimationEnd(Animation animation) { + binding.btnPrev.setVisibility(View.GONE); + } - @Override - public void onAnimationRepeat(Animation animation) { - } - }); + @Override + public void onAnimationRepeat(Animation animation) { + } + }); - binding.btnPrev.startAnimation(fadeIn); - } + return fadeOut; + } + + private @NonNull AlphaAnimation getFadeInAnimation(int duration) { + AlphaAnimation fadeIn = new AlphaAnimation(0.0f, 1.0f); + + fadeIn.setDuration(duration); + + fadeIn.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + binding.btnPrev.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(Animation animation) { + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + + return fadeIn; } private void changeContinueButtonText(int position) { @@ -187,7 +201,7 @@ private void changeContinueButtonText(int position) { new Thread(() -> { try { if (AppUtil.hasStoragePermission()) { - ArrayList wallpaperColors = WallpaperUtil.getWallpaperColors(requireContext()); + ArrayList wallpaperColors = WallpaperColorUtil.getWallpaperColors(requireContext()); RPrefs.putString(WALLPAPER_COLOR_LIST, Const.GSON.toJson(wallpaperColors)); } } catch (Exception ignored) { diff --git a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/SettingsFragment.java b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/SettingsFragment.java index 05ddbece..5e270bb1 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/SettingsFragment.java +++ b/app/src/main/java/com/drdisagree/colorblendr/ui/fragments/SettingsFragment.java @@ -309,8 +309,6 @@ private void backupRestoreSettings(boolean isBackingUp) { dialog.dismiss(); Executors.newSingleThreadExecutor().execute(() -> { try { - RPrefs.clearAllPrefs(); - RPrefs.restorePrefs( Objects.requireNonNull( ColorBlendr.getAppContext() diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/ColorSchemeUtil.java b/app/src/main/java/com/drdisagree/colorblendr/utils/ColorSchemeUtil.java index 3b3b521b..1c180586 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/ColorSchemeUtil.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/ColorSchemeUtil.java @@ -7,7 +7,7 @@ import com.drdisagree.colorblendr.R; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; -import com.drdisagree.colorblendr.utils.monet.scheme.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; import com.drdisagree.colorblendr.utils.monet.scheme.SchemeContent; import com.drdisagree.colorblendr.utils.monet.scheme.SchemeExpressive; import com.drdisagree.colorblendr.utils.monet.scheme.SchemeFidelity; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/ColorUtil.java b/app/src/main/java/com/drdisagree/colorblendr/utils/ColorUtil.java index 753eac5a..36b47580 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/ColorUtil.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/ColorUtil.java @@ -94,7 +94,7 @@ public static ArrayList> generateModifiedColors( }.getType() ); } else { - wallpaperColorList = WallpaperUtil.getWallpaperColors(context); + wallpaperColorList = WallpaperColorUtil.getWallpaperColors(context); } return generateModifiedColors( diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/MiscUtil.java b/app/src/main/java/com/drdisagree/colorblendr/utils/MiscUtil.java index 3c344c61..664919e4 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/MiscUtil.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/MiscUtil.java @@ -1,10 +1,6 @@ package com.drdisagree.colorblendr.utils; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; @@ -20,21 +16,6 @@ public class MiscUtil { - public static ArrayList> convertIntArrayToList(int[][] array) { - ArrayList> result = new ArrayList<>(); - - for (int[] row : array) { - ArrayList rowList = new ArrayList<>(); - for (int value : row) { - rowList.add(value); - } - - result.add(rowList); - } - - return result; - } - public static int[][] convertListToIntArray(ArrayList> arrayList) { return arrayList.stream() .map(row -> row.stream().mapToInt(Integer::intValue).toArray()) @@ -73,26 +54,4 @@ public static JSONObject mergeJsonObjects(JSONObject target, JSONObject source) } return target; } - - public static Bitmap drawableToBitmap(Drawable drawable) { - Bitmap bitmap; - - if (drawable instanceof BitmapDrawable bitmapDrawable) { - if (bitmapDrawable.getBitmap() != null) { - return bitmapDrawable.getBitmap(); - } - } - - if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { - bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } else { - bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - } - - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - - return bitmap; - } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/OverlayManager.java b/app/src/main/java/com/drdisagree/colorblendr/utils/OverlayManager.java index f1e6055f..3cb6e633 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/OverlayManager.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/OverlayManager.java @@ -4,7 +4,6 @@ import static com.drdisagree.colorblendr.common.Const.FABRICATED_OVERLAY_NAME_SYSTEM; import static com.drdisagree.colorblendr.common.Const.FABRICATED_OVERLAY_NAME_SYSTEMUI; import static com.drdisagree.colorblendr.common.Const.FRAMEWORK_PACKAGE; -import static com.drdisagree.colorblendr.common.Const.MANUAL_OVERRIDE_COLORS; import static com.drdisagree.colorblendr.common.Const.MONET_ACCENT_SATURATION; import static com.drdisagree.colorblendr.common.Const.MONET_ACCURATE_SHADES; import static com.drdisagree.colorblendr.common.Const.MONET_BACKGROUND_LIGHTNESS; @@ -173,7 +172,7 @@ public static void unregisterFabricatedOverlay(String packageName) { } public static void applyFabricatedColors(Context context) { - if (!RPrefs.getBoolean(THEMING_ENABLED, true) || !RPrefs.getBoolean(SHIZUKU_THEMING_ENABLED, true)) { + if (!RPrefs.getBoolean(THEMING_ENABLED, true) && !RPrefs.getBoolean(SHIZUKU_THEMING_ENABLED, true)) { return; } @@ -190,7 +189,6 @@ public static void applyFabricatedColors(Context context) { int monetBackgroundLightness = RPrefs.getInt(MONET_BACKGROUND_LIGHTNESS, 100); boolean pitchBlackTheme = RPrefs.getBoolean(MONET_PITCH_BLACK_THEME, false); boolean accurateShades = RPrefs.getBoolean(MONET_ACCURATE_SHADES, true); - boolean overrideColors = RPrefs.getBoolean(MANUAL_OVERRIDE_COLORS, false); ArrayList> paletteLight = ColorUtil.generateModifiedColors( context, @@ -257,22 +255,6 @@ public static void applyFabricatedColors(Context context) { fabricatedOverlays.get(0).setColor(colorNames[4][11], Color.BLACK); } - // Override white and black shades - if (overrideColors) { - for (String[] colorName : colorNames) { - int overriddenColorWhite = RPrefs.getInt(colorName[0], Integer.MIN_VALUE); - int overriddenColorBlack = RPrefs.getInt(colorName[colorName.length - 1], Integer.MIN_VALUE); - - if (overriddenColorWhite != Integer.MIN_VALUE) { - fabricatedOverlays.get(0).setColor(colorName[0], overriddenColorWhite); - } - - if (overriddenColorBlack != Integer.MIN_VALUE) { - fabricatedOverlays.get(0).setColor(colorName[colorName.length - 1], overriddenColorBlack); - } - } - } - if (!RPrefs.getBoolean(TINT_TEXT_COLOR, true)) { fabricatedOverlays.get(0).setColor("text_color_primary_device_default_dark", Color.WHITE); fabricatedOverlays.get(0).setColor("text_color_secondary_device_default_dark", 0xB3FFFFFF); diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperUtil.java b/app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperColorUtil.java similarity index 67% rename from app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperUtil.java rename to app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperColorUtil.java index ce19e5d0..5479a4c0 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperUtil.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/WallpaperColorUtil.java @@ -7,7 +7,9 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Color; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; @@ -16,37 +18,27 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.palette.graphics.Palette; import com.drdisagree.colorblendr.ColorBlendr; import com.drdisagree.colorblendr.common.Const; import com.drdisagree.colorblendr.config.RPrefs; +import com.drdisagree.colorblendr.utils.monet.quantize.QuantizerCelebi; +import com.drdisagree.colorblendr.utils.monet.score.Score; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.stream.Collectors; -import java.util.stream.Stream; -public class WallpaperUtil { +public class WallpaperColorUtil { - private static final String TAG = WallpaperUtil.class.getSimpleName(); + private static final String TAG = WallpaperColorUtil.class.getSimpleName(); private static final int SMALL_SIDE = 128; private static final int MAX_BITMAP_SIZE = 112; private static final int MAX_WALLPAPER_EXTRACTION_AREA = MAX_BITMAP_SIZE * MAX_BITMAP_SIZE; - private static final double MINIMUM_DARKNESS = 0.1; - private static final double MAXIMUM_DARKNESS = 0.8; - private static final float HUE_THRESHOLD = 25f; public static void getAndSaveWallpaperColors(Context context) { if (RPrefs.getInt(MONET_SEED_COLOR, Integer.MIN_VALUE) == Integer.MIN_VALUE && @@ -72,7 +64,7 @@ public static ArrayList getWallpaperColors(Context context) { try { Bitmap wallpaperBitmap = wallpaperFuture.get(); if (wallpaperBitmap != null) { - return WallpaperUtil.getDominantColors(wallpaperBitmap); + return WallpaperColorUtil.getWallpaperColors(wallpaperBitmap); } } catch (ExecutionException | InterruptedException e) { Log.e(TAG, "Error getting wallpaper color", e); @@ -115,7 +107,7 @@ private static Bitmap loadWallpaper(Context context, int which) { WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); if (wallpaperManager.getWallpaperInfo() != null) { - return MiscUtil.drawableToBitmap( + return drawableToBitmap( wallpaperManager.getWallpaperInfo().loadThumbnail( ColorBlendr.getAppContext().getPackageManager() ) @@ -189,9 +181,9 @@ private static Size calculateOptimalSize(int width, int height) { return new Size(newWidth, newHeight); } - private static ArrayList getDominantColors(Bitmap bitmap) { + private static ArrayList getWallpaperColors(Bitmap bitmap) { if (bitmap == null) { - return new ArrayList<>(Collections.singletonList(Color.BLUE)); + return ColorUtil.getMonetAccentColors(); } int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); @@ -200,58 +192,64 @@ private static ArrayList getDominantColors(Bitmap bitmap) { bitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(), optimalSize.getHeight(), false); } - Palette palette = createPalette(bitmap); - List sortedSwatches = sortSwatches(palette.getSwatches()); - - List uniqueSwatches = getUniqueSwatches(palette); - sortedSwatches.addAll(0, uniqueSwatches); + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int[] pixels = new int[width * height]; - List filteredSwatches = filterColors(sortedSwatches); + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); - ArrayList wallpaperColors = filteredSwatches.stream().map(Palette.Swatch::getRgb).collect(Collectors.toCollection(ArrayList::new)); - - return wallpaperColors.isEmpty() ? new ArrayList<>(Collections.singletonList(Color.BLUE)) : wallpaperColors; - } + ArrayList wallpaperColors = new ArrayList<>( + Score.score( + QuantizerCelebi.quantize(pixels, 25) + ) + ); - private static Palette createPalette(Bitmap bitmap) { - return Palette.from(bitmap) - .maximumColorCount(25) - .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA) - .generate(); + return wallpaperColors.isEmpty() ? ColorUtil.getMonetAccentColors() : wallpaperColors; } - private static List sortSwatches(List swatches) { - List sortedSwatches = new ArrayList<>(swatches); - sortedSwatches.sort(Comparator.comparingInt(Palette.Swatch::getPopulation).reversed()); - return sortedSwatches; - } - - private static List getUniqueSwatches(Palette palette) { - return Stream.of( - palette.getVibrantSwatch(), - palette.getDarkVibrantSwatch(), - palette.getLightVibrantSwatch(), - palette.getMutedSwatch(), - palette.getDarkMutedSwatch(), - palette.getLightMutedSwatch() - ).filter(Objects::nonNull).collect(Collectors.toList()); - } + public static Bitmap drawableToBitmap(Drawable drawable) { + Bitmap bitmap; - private static List filterColors(List swatches) { - Set addedHues = new HashSet<>(); - return swatches.stream() - .filter(swatch -> isColorInRange(swatch.getRgb()) && !hasSimilarHue(addedHues, swatch.getRgb())) - .peek(swatch -> addedHues.add(ColorUtil.getHue(swatch.getRgb()))) - .collect(Collectors.toList()); - } + if (drawable instanceof BitmapDrawable bitmapDrawable) { + if (bitmapDrawable.getBitmap() != null) { + return bitmapDrawable.getBitmap(); + } + } - private static boolean hasSimilarHue(Set addedHues, int color) { - float hue = ColorUtil.getHue(color); - return addedHues.stream().anyMatch(addedHue -> Math.abs(hue - addedHue) < HUE_THRESHOLD); - } + int intrinsicWidth = drawable.getIntrinsicWidth(); + int intrinsicHeight = drawable.getIntrinsicHeight(); + + if (intrinsicWidth <= 0 || intrinsicHeight <= 0) { + ArrayList colors = ColorUtil.getMonetAccentColors(); + int colorCount = colors.size(); + + bitmap = Bitmap.createBitmap(colorCount, 1, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + + int colorIndex = 0; + if (colorCount > 0) { + int rectWidth = canvas.getWidth() / colorCount; + for (Integer color : colors) { + canvas.save(); + canvas.clipRect(colorIndex * rectWidth, 0, (colorIndex + 1) * rectWidth, canvas.getHeight()); + canvas.drawColor(color); + canvas.restore(); + colorIndex++; + } + } + } else { + bitmap = createMiniBitmap( + Bitmap.createBitmap( + intrinsicWidth, + intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) + ); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, intrinsicWidth, intrinsicHeight); + drawable.draw(canvas); + } - private static boolean isColorInRange(int color) { - double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; - return darkness >= MINIMUM_DARKNESS && darkness <= MAXIMUM_DARKNESS; + return bitmap; } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/blend/Blend.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/blend/Blend.java index db02626d..eb8fbc86 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/blend/Blend.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/blend/Blend.java @@ -44,10 +44,7 @@ public static int harmonize(int designColor, int sourceColor) { Hct toHct = Hct.fromInt(sourceColor); double differenceDegrees = MathUtils.differenceDegrees(fromHct.getHue(), toHct.getHue()); double rotationDegrees = Math.min(differenceDegrees * 0.5, 15.0); - double outputHue = - MathUtils.sanitizeDegreesDouble( - fromHct.getHue() - + rotationDegrees * MathUtils.rotationDirection(fromHct.getHue(), toHct.getHue())); + double outputHue = MathUtils.sanitizeDegreesDouble(fromHct.getHue() + rotationDegrees * MathUtils.rotationDirection(fromHct.getHue(), toHct.getHue())); return Hct.from(outputHue, fromHct.getChroma(), fromHct.getTone()).toInt(); } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicColor.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicColor.java index 46b89624..f17894de 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicColor.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicColor.java @@ -25,7 +25,6 @@ import com.drdisagree.colorblendr.utils.monet.contrast.Contrast; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; -import com.drdisagree.colorblendr.utils.monet.scheme.DynamicScheme; import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; import java.util.ArrayList; @@ -264,6 +263,69 @@ public static DynamicColor fromArgb(@NonNull String name, int argb) { return DynamicColor.fromPalette(name, (s) -> palette, (s) -> hct.getTone()); } + /** + * Given a background tone, find a foreground tone, while ensuring they reach a contrast ratio + * that is as close to ratio as possible. + */ + public static double foregroundTone(double bgTone, double ratio) { + double lighterTone = Contrast.lighterUnsafe(bgTone, ratio); + double darkerTone = Contrast.darkerUnsafe(bgTone, ratio); + double lighterRatio = Contrast.ratioOfTones(lighterTone, bgTone); + double darkerRatio = Contrast.ratioOfTones(darkerTone, bgTone); + boolean preferLighter = tonePrefersLightForeground(bgTone); + + if (preferLighter) { + // "Neglible difference" handles an edge case where the initial contrast ratio is high + // (ex. 13.0), and the ratio passed to the function is that high ratio, and both the lighter + // and darker ratio fails to pass that ratio. + // + // This was observed with Tonal Spot's On Primary Container turning black momentarily between + // high and max contrast in light mode. PC's standard tone was T90, OPC's was T10, it was + // light mode, and the contrast level was 0.6568521221032331. + boolean negligibleDifference = + Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio && darkerRatio < ratio; + if (lighterRatio >= ratio || lighterRatio >= darkerRatio || negligibleDifference) { + return lighterTone; + } else { + return darkerTone; + } + } else { + return darkerRatio >= ratio || darkerRatio >= lighterRatio ? darkerTone : lighterTone; + } + } + + /** + * Adjust a tone down such that white has 4.5 contrast, if the tone is reasonably close to + * supporting it. + */ + public static double enableLightForeground(double tone) { + if (tonePrefersLightForeground(tone) && !toneAllowsLightForeground(tone)) { + return 49.0; + } + return tone; + } + + /** + * People prefer white foregrounds on ~T60-70. Observed over time, and also by Andrew Somers + * during research for APCA. + * + *

T60 used as to create the smallest discontinuity possible when skipping down to T49 in order + * to ensure light foregrounds. + * + *

Since `tertiaryContainer` in dark monochrome scheme requires a tone of 60, it should not be + * adjusted. Therefore, 60 is excluded here. + */ + public static boolean tonePrefersLightForeground(double tone) { + return Math.round(tone) < 60; + } + + /** + * Tones less than ~T50 always permit white at 4.5 contrast. + */ + public static boolean toneAllowsLightForeground(double tone) { + return Math.round(tone) <= 49; + } + /** * Returns an ARGB integer (i.e. a hex code). * @@ -484,67 +546,4 @@ public double getTone(@NonNull DynamicScheme scheme) { return answer; } } - - /** - * Given a background tone, find a foreground tone, while ensuring they reach a contrast ratio - * that is as close to ratio as possible. - */ - public static double foregroundTone(double bgTone, double ratio) { - double lighterTone = Contrast.lighterUnsafe(bgTone, ratio); - double darkerTone = Contrast.darkerUnsafe(bgTone, ratio); - double lighterRatio = Contrast.ratioOfTones(lighterTone, bgTone); - double darkerRatio = Contrast.ratioOfTones(darkerTone, bgTone); - boolean preferLighter = tonePrefersLightForeground(bgTone); - - if (preferLighter) { - // "Neglible difference" handles an edge case where the initial contrast ratio is high - // (ex. 13.0), and the ratio passed to the function is that high ratio, and both the lighter - // and darker ratio fails to pass that ratio. - // - // This was observed with Tonal Spot's On Primary Container turning black momentarily between - // high and max contrast in light mode. PC's standard tone was T90, OPC's was T10, it was - // light mode, and the contrast level was 0.6568521221032331. - boolean negligibleDifference = - Math.abs(lighterRatio - darkerRatio) < 0.1 && lighterRatio < ratio && darkerRatio < ratio; - if (lighterRatio >= ratio || lighterRatio >= darkerRatio || negligibleDifference) { - return lighterTone; - } else { - return darkerTone; - } - } else { - return darkerRatio >= ratio || darkerRatio >= lighterRatio ? darkerTone : lighterTone; - } - } - - /** - * Adjust a tone down such that white has 4.5 contrast, if the tone is reasonably close to - * supporting it. - */ - public static double enableLightForeground(double tone) { - if (tonePrefersLightForeground(tone) && !toneAllowsLightForeground(tone)) { - return 49.0; - } - return tone; - } - - /** - * People prefer white foregrounds on ~T60-70. Observed over time, and also by Andrew Somers - * during research for APCA. - * - *

T60 used as to create the smallest discontinuity possible when skipping down to T49 in order - * to ensure light foregrounds. - * - *

Since `tertiaryContainer` in dark monochrome scheme requires a tone of 60, it should not be - * adjusted. Therefore, 60 is excluded here. - */ - public static boolean tonePrefersLightForeground(double tone) { - return Math.round(tone) < 60; - } - - /** - * Tones less than ~T50 always permit white at 4.5 contrast. - */ - public static boolean toneAllowsLightForeground(double tone) { - return Math.round(tone) <= 49; - } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicScheme.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicScheme.java new file mode 100644 index 00000000..37b12243 --- /dev/null +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/DynamicScheme.java @@ -0,0 +1,340 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.drdisagree.colorblendr.utils.monet.dynamiccolor; + +import com.drdisagree.colorblendr.utils.monet.hct.Hct; +import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; +import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; + +/** + * Provides important settings for creating colors dynamically, and 6 color palettes. Requires: 1. A + * color. (source color) 2. A theme. (Variant) 3. Whether or not its dark mode. 4. Contrast level. + * (-1 to 1, currently contrast ratio 3.0 and 7.0) + */ +public class DynamicScheme { + public final int sourceColorArgb; + public final Hct sourceColorHct; + public final Variant variant; + public final boolean isDark; + public final double contrastLevel; + + public final TonalPalette primaryPalette; + public final TonalPalette secondaryPalette; + public final TonalPalette tertiaryPalette; + public final TonalPalette neutralPalette; + public final TonalPalette neutralVariantPalette; + public final TonalPalette errorPalette; + + public DynamicScheme(Hct sourceColorHct, Variant variant, boolean isDark, double contrastLevel, TonalPalette primaryPalette, TonalPalette secondaryPalette, TonalPalette tertiaryPalette, TonalPalette neutralPalette, TonalPalette neutralVariantPalette) { + this.sourceColorArgb = sourceColorHct.toInt(); + this.sourceColorHct = sourceColorHct; + this.variant = variant; + this.isDark = isDark; + this.contrastLevel = contrastLevel; + + this.primaryPalette = primaryPalette; + this.secondaryPalette = secondaryPalette; + this.tertiaryPalette = tertiaryPalette; + this.neutralPalette = neutralPalette; + this.neutralVariantPalette = neutralVariantPalette; + this.errorPalette = TonalPalette.fromHueAndChroma(25.0, 84.0); + } + + /** + * Given a set of hues and set of hue rotations, locate which hues the source color's hue is + * between, apply the rotation at the same index as the first hue in the range, and return the + * rotated hue. + * + * @param sourceColorHct The color whose hue should be rotated. + * @param hues A set of hues. + * @param rotations A set of hue rotations. + * @return Color's hue with a rotation applied. + */ + public static double getRotatedHue(Hct sourceColorHct, double[] hues, double[] rotations) { + final double sourceHue = sourceColorHct.getHue(); + if (rotations.length == 1) { + return MathUtils.sanitizeDegreesDouble(sourceHue + rotations[0]); + } + final int size = hues.length; + for (int i = 0; i <= (size - 2); i++) { + final double thisHue = hues[i]; + final double nextHue = hues[i + 1]; + if (thisHue < sourceHue && sourceHue < nextHue) { + return MathUtils.sanitizeDegreesDouble(sourceHue + rotations[i]); + } + } + // If this statement executes, something is wrong, there should have been a rotation + // found using the arrays. + return sourceHue; + } + + public Hct getHct(DynamicColor dynamicColor) { + return dynamicColor.getHct(this); + } + + public int getArgb(DynamicColor dynamicColor) { + return dynamicColor.getArgb(this); + } + + public int getPrimaryPaletteKeyColor() { + return getArgb(new MaterialDynamicColors().primaryPaletteKeyColor()); + } + + public int getSecondaryPaletteKeyColor() { + return getArgb(new MaterialDynamicColors().secondaryPaletteKeyColor()); + } + + public int getTertiaryPaletteKeyColor() { + return getArgb(new MaterialDynamicColors().tertiaryPaletteKeyColor()); + } + + public int getNeutralPaletteKeyColor() { + return getArgb(new MaterialDynamicColors().neutralPaletteKeyColor()); + } + + public int getNeutralVariantPaletteKeyColor() { + return getArgb(new MaterialDynamicColors().neutralVariantPaletteKeyColor()); + } + + public int getBackground() { + return getArgb(new MaterialDynamicColors().background()); + } + + public int getOnBackground() { + return getArgb(new MaterialDynamicColors().onBackground()); + } + + public int getSurface() { + return getArgb(new MaterialDynamicColors().surface()); + } + + public int getSurfaceDim() { + return getArgb(new MaterialDynamicColors().surfaceDim()); + } + + public int getSurfaceBright() { + return getArgb(new MaterialDynamicColors().surfaceBright()); + } + + public int getSurfaceContainerLowest() { + return getArgb(new MaterialDynamicColors().surfaceContainerLowest()); + } + + public int getSurfaceContainerLow() { + return getArgb(new MaterialDynamicColors().surfaceContainerLow()); + } + + public int getSurfaceContainer() { + return getArgb(new MaterialDynamicColors().surfaceContainer()); + } + + public int getSurfaceContainerHigh() { + return getArgb(new MaterialDynamicColors().surfaceContainerHigh()); + } + + public int getSurfaceContainerHighest() { + return getArgb(new MaterialDynamicColors().surfaceContainerHighest()); + } + + public int getOnSurface() { + return getArgb(new MaterialDynamicColors().onSurface()); + } + + public int getSurfaceVariant() { + return getArgb(new MaterialDynamicColors().surfaceVariant()); + } + + public int getOnSurfaceVariant() { + return getArgb(new MaterialDynamicColors().onSurfaceVariant()); + } + + public int getInverseSurface() { + return getArgb(new MaterialDynamicColors().inverseSurface()); + } + + public int getInverseOnSurface() { + return getArgb(new MaterialDynamicColors().inverseOnSurface()); + } + + public int getOutline() { + return getArgb(new MaterialDynamicColors().outline()); + } + + public int getOutlineVariant() { + return getArgb(new MaterialDynamicColors().outlineVariant()); + } + + public int getShadow() { + return getArgb(new MaterialDynamicColors().shadow()); + } + + public int getScrim() { + return getArgb(new MaterialDynamicColors().scrim()); + } + + public int getSurfaceTint() { + return getArgb(new MaterialDynamicColors().surfaceTint()); + } + + public int getPrimary() { + return getArgb(new MaterialDynamicColors().primary()); + } + + public int getOnPrimary() { + return getArgb(new MaterialDynamicColors().onPrimary()); + } + + public int getPrimaryContainer() { + return getArgb(new MaterialDynamicColors().primaryContainer()); + } + + public int getOnPrimaryContainer() { + return getArgb(new MaterialDynamicColors().onPrimaryContainer()); + } + + public int getInversePrimary() { + return getArgb(new MaterialDynamicColors().inversePrimary()); + } + + public int getSecondary() { + return getArgb(new MaterialDynamicColors().secondary()); + } + + public int getOnSecondary() { + return getArgb(new MaterialDynamicColors().onSecondary()); + } + + public int getSecondaryContainer() { + return getArgb(new MaterialDynamicColors().secondaryContainer()); + } + + public int getOnSecondaryContainer() { + return getArgb(new MaterialDynamicColors().onSecondaryContainer()); + } + + public int getTertiary() { + return getArgb(new MaterialDynamicColors().tertiary()); + } + + public int getOnTertiary() { + return getArgb(new MaterialDynamicColors().onTertiary()); + } + + public int getTertiaryContainer() { + return getArgb(new MaterialDynamicColors().tertiaryContainer()); + } + + public int getOnTertiaryContainer() { + return getArgb(new MaterialDynamicColors().onTertiaryContainer()); + } + + public int getError() { + return getArgb(new MaterialDynamicColors().error()); + } + + public int getOnError() { + return getArgb(new MaterialDynamicColors().onError()); + } + + public int getErrorContainer() { + return getArgb(new MaterialDynamicColors().errorContainer()); + } + + public int getOnErrorContainer() { + return getArgb(new MaterialDynamicColors().onErrorContainer()); + } + + public int getPrimaryFixed() { + return getArgb(new MaterialDynamicColors().primaryFixed()); + } + + public int getPrimaryFixedDim() { + return getArgb(new MaterialDynamicColors().primaryFixedDim()); + } + + public int getOnPrimaryFixed() { + return getArgb(new MaterialDynamicColors().onPrimaryFixed()); + } + + public int getOnPrimaryFixedVariant() { + return getArgb(new MaterialDynamicColors().onPrimaryFixedVariant()); + } + + public int getSecondaryFixed() { + return getArgb(new MaterialDynamicColors().secondaryFixed()); + } + + public int getSecondaryFixedDim() { + return getArgb(new MaterialDynamicColors().secondaryFixedDim()); + } + + public int getOnSecondaryFixed() { + return getArgb(new MaterialDynamicColors().onSecondaryFixed()); + } + + public int getOnSecondaryFixedVariant() { + return getArgb(new MaterialDynamicColors().onSecondaryFixedVariant()); + } + + public int getTertiaryFixed() { + return getArgb(new MaterialDynamicColors().tertiaryFixed()); + } + + public int getTertiaryFixedDim() { + return getArgb(new MaterialDynamicColors().tertiaryFixedDim()); + } + + public int getOnTertiaryFixed() { + return getArgb(new MaterialDynamicColors().onTertiaryFixed()); + } + + public int getOnTertiaryFixedVariant() { + return getArgb(new MaterialDynamicColors().onTertiaryFixedVariant()); + } + + public int getControlActivated() { + return getArgb(new MaterialDynamicColors().controlActivated()); + } + + public int getControlNormal() { + return getArgb(new MaterialDynamicColors().controlNormal()); + } + + public int getControlHighlight() { + return getArgb(new MaterialDynamicColors().controlHighlight()); + } + + public int getTextPrimaryInverse() { + return getArgb(new MaterialDynamicColors().textPrimaryInverse()); + } + + public int getTextSecondaryAndTertiaryInverse() { + return getArgb(new MaterialDynamicColors().textSecondaryAndTertiaryInverse()); + } + + public int getTextPrimaryInverseDisableOnly() { + return getArgb(new MaterialDynamicColors().textPrimaryInverseDisableOnly()); + } + + public int getTextSecondaryAndTertiaryInverseDisabled() { + return getArgb(new MaterialDynamicColors().textSecondaryAndTertiaryInverseDisabled()); + } + + public int getTextHintInverse() { + return getArgb(new MaterialDynamicColors().textHintInverse()); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/MaterialDynamicColors.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/MaterialDynamicColors.java index 0e139b41..e6ad23f8 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/MaterialDynamicColors.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/MaterialDynamicColors.java @@ -20,8 +20,6 @@ import com.drdisagree.colorblendr.utils.monet.dislike.DislikeAnalyzer; import com.drdisagree.colorblendr.utils.monet.hct.Hct; -import com.drdisagree.colorblendr.utils.monet.scheme.DynamicScheme; -import com.drdisagree.colorblendr.utils.monet.scheme.Variant; /** * Named colors, otherwise known as tokens, or roles, in the Material Design system. @@ -49,6 +47,38 @@ public MaterialDynamicColors(boolean isExtendedFidelity) { this.isExtendedFidelity = isExtendedFidelity; } + private static boolean isMonochrome(DynamicScheme scheme) { + return scheme.variant == Variant.MONOCHROME; + } + + static double findDesiredChromaByTone(double hue, double chroma, double tone, boolean byDecreasingTone) { + double answer = tone; + + Hct closestToChroma = Hct.from(hue, chroma, tone); + if (closestToChroma.getChroma() < chroma) { + double chromaPeak = closestToChroma.getChroma(); + while (closestToChroma.getChroma() < chroma) { + answer += byDecreasingTone ? -1.0 : 1.0; + Hct potentialSolution = Hct.from(hue, chroma, answer); + if (chromaPeak > potentialSolution.getChroma()) { + break; + } + if (Math.abs(potentialSolution.getChroma() - chroma) < 0.4) { + break; + } + + double potentialDelta = Math.abs(potentialSolution.getChroma() - chroma); + double currentDelta = Math.abs(closestToChroma.getChroma() - chroma); + if (potentialDelta < currentDelta) { + closestToChroma = potentialSolution; + } + chromaPeak = Math.max(chromaPeak, potentialSolution.getChroma()); + } + } + + return answer; + } + @NonNull public DynamicColor highestSurface(@NonNull DynamicScheme s) { return s.isDark ? surfaceBright() : surfaceDim(); @@ -139,8 +169,7 @@ public DynamicColor surfaceDim() { return new DynamicColor( /* name= */ "surface_dim", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark ? 6.0 : new ContrastCurve(87.0, 87.0, 80.0, 75.0).get(s.contrastLevel), + /* tone= */ (s) -> s.isDark ? 6.0 : new ContrastCurve(87.0, 87.0, 80.0, 75.0).get(s.contrastLevel), /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -153,8 +182,7 @@ public DynamicColor surfaceBright() { return new DynamicColor( /* name= */ "surface_bright", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark ? new ContrastCurve(24.0, 24.0, 29.0, 34.0).get(s.contrastLevel) : 98.0, + /* tone= */ (s) -> s.isDark ? new ContrastCurve(24.0, 24.0, 29.0, 34.0).get(s.contrastLevel) : 98.0, /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -167,8 +195,7 @@ public DynamicColor surfaceContainerLowest() { return new DynamicColor( /* name= */ "surface_container_lowest", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark ? new ContrastCurve(4.0, 4.0, 2.0, 0.0).get(s.contrastLevel) : 100.0, + /* tone= */ (s) -> s.isDark ? new ContrastCurve(4.0, 4.0, 2.0, 0.0).get(s.contrastLevel) : 100.0, /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -181,10 +208,7 @@ public DynamicColor surfaceContainerLow() { return new DynamicColor( /* name= */ "surface_container_low", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark - ? new ContrastCurve(10.0, 10.0, 11.0, 12.0).get(s.contrastLevel) - : new ContrastCurve(96.0, 96.0, 96.0, 95.0).get(s.contrastLevel), + /* tone= */ (s) -> s.isDark ? new ContrastCurve(10.0, 10.0, 11.0, 12.0).get(s.contrastLevel) : new ContrastCurve(96.0, 96.0, 96.0, 95.0).get(s.contrastLevel), /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -197,10 +221,7 @@ public DynamicColor surfaceContainer() { return new DynamicColor( /* name= */ "surface_container", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark - ? new ContrastCurve(12.0, 12.0, 16.0, 20.0).get(s.contrastLevel) - : new ContrastCurve(94.0, 94.0, 92.0, 90.0).get(s.contrastLevel), + /* tone= */ (s) -> s.isDark ? new ContrastCurve(12.0, 12.0, 16.0, 20.0).get(s.contrastLevel) : new ContrastCurve(94.0, 94.0, 92.0, 90.0).get(s.contrastLevel), /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -213,10 +234,7 @@ public DynamicColor surfaceContainerHigh() { return new DynamicColor( /* name= */ "surface_container_high", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark - ? new ContrastCurve(17.0, 17.0, 21.0, 25.0).get(s.contrastLevel) - : new ContrastCurve(92.0, 92.0, 88.0, 85.0).get(s.contrastLevel), + /* tone= */ (s) -> s.isDark ? new ContrastCurve(17.0, 17.0, 21.0, 25.0).get(s.contrastLevel) : new ContrastCurve(92.0, 92.0, 88.0, 85.0).get(s.contrastLevel), /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -229,10 +247,7 @@ public DynamicColor surfaceContainerHighest() { return new DynamicColor( /* name= */ "surface_container_highest", /* palette= */ (s) -> s.neutralPalette, - /* tone= */ (s) -> - s.isDark - ? new ContrastCurve(22.0, 22.0, 26.0, 30.0).get(s.contrastLevel) - : new ContrastCurve(90.0, 90.0, 84.0, 80.0).get(s.contrastLevel), + /* tone= */ (s) -> s.isDark ? new ContrastCurve(22.0, 22.0, 26.0, 30.0).get(s.contrastLevel) : new ContrastCurve(90.0, 90.0, 84.0, 80.0).get(s.contrastLevel), /* isBackground= */ true, /* background= */ null, /* secondBackground= */ null, @@ -385,8 +400,7 @@ public DynamicColor primary() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(3.0, 4.5, 7.0, 7.0), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(primaryContainer(), primary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(primaryContainer(), primary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -425,8 +439,7 @@ public DynamicColor primaryContainer() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(primaryContainer(), primary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(primaryContainer(), primary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -473,8 +486,7 @@ public DynamicColor secondary() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(3.0, 4.5, 7.0, 7.0), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(secondaryContainer(), secondary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(secondaryContainer(), secondary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -509,15 +521,13 @@ public DynamicColor secondaryContainer() { if (!isFidelity(s)) { return initialTone; } - return findDesiredChromaByTone( - s.secondaryPalette.getHue(), s.secondaryPalette.getChroma(), initialTone, !s.isDark); + return findDesiredChromaByTone(s.secondaryPalette.getHue(), s.secondaryPalette.getChroma(), initialTone, !s.isDark); }, /* isBackground= */ true, /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(secondaryContainer(), secondary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(secondaryContainer(), secondary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -553,8 +563,7 @@ public DynamicColor tertiary() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(3.0, 4.5, 7.0, 7.0), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(tertiaryContainer(), tertiary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(tertiaryContainer(), tertiary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -594,8 +603,7 @@ public DynamicColor tertiaryContainer() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(tertiaryContainer(), tertiary(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(tertiaryContainer(), tertiary(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -629,8 +637,7 @@ public DynamicColor error() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(3.0, 4.5, 7.0, 7.0), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(errorContainer(), error(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(errorContainer(), error(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -656,8 +663,7 @@ public DynamicColor errorContainer() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(errorContainer(), error(), 10.0, TonePolarity.NEARER, false)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(errorContainer(), error(), 10.0, TonePolarity.NEARER, false)); } @NonNull @@ -683,8 +689,7 @@ public DynamicColor primaryFixed() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(primaryFixed(), primaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(primaryFixed(), primaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -697,8 +702,7 @@ public DynamicColor primaryFixedDim() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair(primaryFixed(), primaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(primaryFixed(), primaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -737,9 +741,7 @@ public DynamicColor secondaryFixed() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair( - secondaryFixed(), secondaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(secondaryFixed(), secondaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -752,9 +754,7 @@ public DynamicColor secondaryFixedDim() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair( - secondaryFixed(), secondaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(secondaryFixed(), secondaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -793,9 +793,7 @@ public DynamicColor tertiaryFixed() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair( - tertiaryFixed(), tertiaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(tertiaryFixed(), tertiaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -808,9 +806,7 @@ public DynamicColor tertiaryFixedDim() { /* background= */ this::highestSurface, /* secondBackground= */ null, /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 3.0, 4.5), - /* toneDeltaPair= */ (s) -> - new ToneDeltaPair( - tertiaryFixed(), tertiaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); + /* toneDeltaPair= */ (s) -> new ToneDeltaPair(tertiaryFixed(), tertiaryFixedDim(), 10.0, TonePolarity.LIGHTER, true)); } @NonNull @@ -854,8 +850,7 @@ public DynamicColor onTertiaryFixedVariant() { // Therefore, this is a duplicated version of Primary Container. @NonNull public DynamicColor controlActivated() { - return DynamicColor.fromPalette( - "control_activated", (s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0); + return DynamicColor.fromPalette("control_activated", (s) -> s.primaryPalette, (s) -> s.isDark ? 30.0 : 90.0); } // colorControlNormal documented as textColorSecondary in M3 & GM3. @@ -863,8 +858,7 @@ public DynamicColor controlActivated() { // which is Neutral Variant T30/80 in light/dark. @NonNull public DynamicColor controlNormal() { - return DynamicColor.fromPalette( - "control_normal", (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0); + return DynamicColor.fromPalette("control_normal", (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 80.0 : 30.0); } // colorControlHighlight documented, in both M3 & GM3: @@ -892,85 +886,39 @@ public DynamicColor controlHighlight() { // textColorPrimaryInverse documented, in both M3 & GM3, documented as N10/N90. @NonNull public DynamicColor textPrimaryInverse() { - return DynamicColor.fromPalette( - "text_primary_inverse", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + return DynamicColor.fromPalette("text_primary_inverse", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); } // textColorSecondaryInverse and textColorTertiaryInverse both documented, in both M3 & GM3, as // NV30/NV80 @NonNull public DynamicColor textSecondaryAndTertiaryInverse() { - return DynamicColor.fromPalette( - "text_secondary_and_tertiary_inverse", - (s) -> s.neutralVariantPalette, - (s) -> s.isDark ? 30.0 : 80.0); + return DynamicColor.fromPalette("text_secondary_and_tertiary_inverse", (s) -> s.neutralVariantPalette, (s) -> s.isDark ? 30.0 : 80.0); } // textColorPrimaryInverseDisableOnly documented, in both M3 & GM3, as N10/N90 @NonNull public DynamicColor textPrimaryInverseDisableOnly() { - return DynamicColor.fromPalette( - "text_primary_inverse_disable_only", - (s) -> s.neutralPalette, - (s) -> s.isDark ? 10.0 : 90.0); + return DynamicColor.fromPalette("text_primary_inverse_disable_only", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); } // textColorSecondaryInverse and textColorTertiaryInverse in disabled state both documented, // in both M3 & GM3, as N10/N90 @NonNull public DynamicColor textSecondaryAndTertiaryInverseDisabled() { - return DynamicColor.fromPalette( - "text_secondary_and_tertiary_inverse_disabled", - (s) -> s.neutralPalette, - (s) -> s.isDark ? 10.0 : 90.0); + return DynamicColor.fromPalette("text_secondary_and_tertiary_inverse_disabled", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); } // textColorHintInverse documented, in both M3 & GM3, as N10/N90 @NonNull public DynamicColor textHintInverse() { - return DynamicColor.fromPalette( - "text_hint_inverse", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); + return DynamicColor.fromPalette("text_hint_inverse", (s) -> s.neutralPalette, (s) -> s.isDark ? 10.0 : 90.0); } private boolean isFidelity(DynamicScheme scheme) { - if (this.isExtendedFidelity - && scheme.variant != Variant.MONOCHROME - && scheme.variant != Variant.NEUTRAL) { + if (this.isExtendedFidelity && scheme.variant != Variant.MONOCHROME && scheme.variant != Variant.NEUTRAL) { return true; } return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT; } - - private static boolean isMonochrome(DynamicScheme scheme) { - return scheme.variant == Variant.MONOCHROME; - } - - static double findDesiredChromaByTone( - double hue, double chroma, double tone, boolean byDecreasingTone) { - double answer = tone; - - Hct closestToChroma = Hct.from(hue, chroma, tone); - if (closestToChroma.getChroma() < chroma) { - double chromaPeak = closestToChroma.getChroma(); - while (closestToChroma.getChroma() < chroma) { - answer += byDecreasingTone ? -1.0 : 1.0; - Hct potentialSolution = Hct.from(hue, chroma, answer); - if (chromaPeak > potentialSolution.getChroma()) { - break; - } - if (Math.abs(potentialSolution.getChroma() - chroma) < 0.4) { - break; - } - - double potentialDelta = Math.abs(potentialSolution.getChroma() - chroma); - double currentDelta = Math.abs(closestToChroma.getChroma() - chroma); - if (potentialDelta < currentDelta) { - closestToChroma = potentialSolution; - } - chromaPeak = Math.max(chromaPeak, potentialSolution.getChroma()); - } - } - - return answer; - } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/ToneDeltaPair.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/ToneDeltaPair.java index e6cda115..1296a6ac 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/ToneDeltaPair.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/ToneDeltaPair.java @@ -73,12 +73,7 @@ public final class ToneDeltaPair { * @param stayTogether Whether these two roles should stay on the same side of the "awkward zone" * (T50-59). This is necessary for certain cases where one role has two backgrounds. */ - public ToneDeltaPair( - DynamicColor roleA, - DynamicColor roleB, - double delta, - TonePolarity polarity, - boolean stayTogether) { + public ToneDeltaPair(DynamicColor roleA, DynamicColor roleB, double delta, TonePolarity polarity, boolean stayTogether) { this.roleA = roleA; this.roleB = roleB; this.delta = delta; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/TonePolarity.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/TonePolarity.java index 98b860fe..d310e072 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/TonePolarity.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/TonePolarity.java @@ -26,8 +26,5 @@ *

See `ToneDeltaPair` for details. */ public enum TonePolarity { - DARKER, - LIGHTER, - NEARER, - FARTHER + DARKER, LIGHTER, NEARER, FARTHER } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Variant.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/Variant.java similarity index 78% rename from app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Variant.java rename to app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/Variant.java index 275d22a9..c8728305 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Variant.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/dynamiccolor/Variant.java @@ -13,19 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.drdisagree.colorblendr.utils.monet.scheme; +package com.drdisagree.colorblendr.utils.monet.dynamiccolor; /** * Themes for Dynamic Color. */ public enum Variant { - MONOCHROME, - NEUTRAL, - TONAL_SPOT, - VIBRANT, - EXPRESSIVE, - FIDELITY, - CONTENT, - RAINBOW, - FRUIT_SALAD + MONOCHROME, NEUTRAL, TONAL_SPOT, VIBRANT, EXPRESSIVE, FIDELITY, CONTENT, RAINBOW, FRUIT_SALAD } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Cam16.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Cam16.java index f6662ac8..7a6eb481 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Cam16.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Cam16.java @@ -37,18 +37,10 @@ */ public final class Cam16 { // Transforms XYZ color space coordinates to 'cone'/'RGB' responses in CAM16. - static final double[][] XYZ_TO_CAM16RGB = { - {0.401288, 0.650173, -0.051461}, - {-0.250268, 1.204414, 0.045854}, - {-0.002079, 0.048952, 0.953127} - }; + static final double[][] XYZ_TO_CAM16RGB = {{0.401288, 0.650173, -0.051461}, {-0.250268, 1.204414, 0.045854}, {-0.002079, 0.048952, 0.953127}}; // Transforms 'cone'/'RGB' responses in CAM16 to XYZ color space coordinates. - static final double[][] CAM16RGB_TO_XYZ = { - {1.8620678, -1.0112547, 0.14918678}, - {0.38752654, 0.62144744, -0.00897398}, - {-0.01584150, -0.03412294, 1.0499644} - }; + static final double[][] CAM16RGB_TO_XYZ = {{1.8620678, -1.0112547, 0.14918678}, {0.38752654, 0.62144744, -0.00897398}, {-0.01584150, -0.03412294, 1.0499644}}; // CAM16 color dimensions, see getters for documentation. private final double hue; @@ -83,16 +75,7 @@ public final class Cam16 { * @param astar CAM16-UCS a coordinate * @param bstar CAM16-UCS b coordinate */ - private Cam16( - double hue, - double chroma, - double j, - double q, - double m, - double s, - double jstar, - double astar, - double bstar) { + private Cam16(double hue, double chroma, double j, double q, double m, double s, double jstar, double astar, double bstar) { this.hue = hue; this.chroma = chroma; this.j = j; @@ -138,8 +121,7 @@ static Cam16 fromIntInViewingConditions(int argb, ViewingConditions viewingCondi return fromXyzInViewingConditions(x, y, z, viewingConditions); } - static Cam16 fromXyzInViewingConditions( - double x, double y, double z, ViewingConditions viewingConditions) { + static Cam16 fromXyzInViewingConditions(double x, double y, double z, ViewingConditions viewingConditions) { // Transform XYZ to 'cone'/'rgb' responses double[][] matrix = XYZ_TO_CAM16RGB; double rT = (x * matrix[0][0]) + (y * matrix[0][1]) + (z * matrix[0][2]); @@ -171,40 +153,26 @@ static Cam16 fromXyzInViewingConditions( // hue double atan2 = Math.atan2(b, a); double atanDegrees = Math.toDegrees(atan2); - double hue = - atanDegrees < 0 - ? atanDegrees + 360.0 - : atanDegrees >= 360 ? atanDegrees - 360.0 : atanDegrees; + double hue = atanDegrees < 0 ? atanDegrees + 360.0 : atanDegrees >= 360 ? atanDegrees - 360.0 : atanDegrees; double hueRadians = Math.toRadians(hue); // achromatic response to color double ac = p2 * viewingConditions.getNbb(); // CAM16 lightness and brightness - double j = - 100.0 - * Math.pow( - ac / viewingConditions.getAw(), - viewingConditions.getC() * viewingConditions.getZ()); - double q = - 4.0 - / viewingConditions.getC() - * Math.sqrt(j / 100.0) - * (viewingConditions.getAw() + 4.0) - * viewingConditions.getFlRoot(); + double j = 100.0 * Math.pow(ac / viewingConditions.getAw(), viewingConditions.getC() * viewingConditions.getZ()); + double q = 4.0 / viewingConditions.getC() * Math.sqrt(j / 100.0) * (viewingConditions.getAw() + 4.0) * viewingConditions.getFlRoot(); // CAM16 chroma, colorfulness, and saturation. double huePrime = (hue < 20.14) ? hue + 360 : hue; double eHue = 0.25 * (Math.cos(Math.toRadians(huePrime) + 2.0) + 3.8); double p1 = 50000.0 / 13.0 * eHue * viewingConditions.getNc() * viewingConditions.getNcb(); double t = p1 * Math.hypot(a, b) / (u + 0.305); - double alpha = - Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73) * Math.pow(t, 0.9); + double alpha = Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73) * Math.pow(t, 0.9); // CAM16 chroma, colorfulness, saturation double c = alpha * Math.sqrt(j / 100.0); double m = c * viewingConditions.getFlRoot(); - double s = - 50.0 * Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0)); + double s = 50.0 * Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0)); // CAM16-UCS components double jstar = (1.0 + 100.0 * 0.007) * j / (1.0 + 0.007 * j); @@ -230,18 +198,11 @@ static Cam16 fromJch(double j, double c, double h) { * @param h CAM16 hue * @param viewingConditions Information about the environment where the color was observed. */ - private static Cam16 fromJchInViewingConditions( - double j, double c, double h, ViewingConditions viewingConditions) { - double q = - 4.0 - / viewingConditions.getC() - * Math.sqrt(j / 100.0) - * (viewingConditions.getAw() + 4.0) - * viewingConditions.getFlRoot(); + private static Cam16 fromJchInViewingConditions(double j, double c, double h, ViewingConditions viewingConditions) { + double q = 4.0 / viewingConditions.getC() * Math.sqrt(j / 100.0) * (viewingConditions.getAw() + 4.0) * viewingConditions.getFlRoot(); double m = c * viewingConditions.getFlRoot(); double alpha = c / Math.sqrt(j / 100.0); - double s = - 50.0 * Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0)); + double s = 50.0 * Math.sqrt((alpha * viewingConditions.getC()) / (viewingConditions.getAw() + 4.0)); double hueRadians = Math.toRadians(h); double jstar = (1.0 + 100.0 * 0.007) * j / (1.0 + 0.007 * j); @@ -275,8 +236,7 @@ public static Cam16 fromUcs(double jstar, double astar, double bstar) { * axis. * @param viewingConditions Information about the environment where the color was observed. */ - public static Cam16 fromUcsInViewingConditions( - double jstar, double astar, double bstar, ViewingConditions viewingConditions) { + public static Cam16 fromUcsInViewingConditions(double jstar, double astar, double bstar, ViewingConditions viewingConditions) { double m = Math.hypot(astar, bstar); double m2 = Math.expm1(m * 0.0228) / 0.0228; @@ -396,18 +356,13 @@ int viewed(ViewingConditions viewingConditions) { } double[] xyzInViewingConditions(ViewingConditions viewingConditions, double[] returnArray) { - double alpha = - (getChroma() == 0.0 || getJ() == 0.0) ? 0.0 : getChroma() / Math.sqrt(getJ() / 100.0); + double alpha = (getChroma() == 0.0 || getJ() == 0.0) ? 0.0 : getChroma() / Math.sqrt(getJ() / 100.0); - double t = - Math.pow( - alpha / Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73), 1.0 / 0.9); + double t = Math.pow(alpha / Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73), 1.0 / 0.9); double hRad = Math.toRadians(getHue()); double eHue = 0.25 * (Math.cos(hRad + 2.0) + 3.8); - double ac = - viewingConditions.getAw() - * Math.pow(getJ() / 100.0, 1.0 / viewingConditions.getC() / viewingConditions.getZ()); + double ac = viewingConditions.getAw() * Math.pow(getJ() / 100.0, 1.0 / viewingConditions.getC() / viewingConditions.getZ()); double p1 = eHue * (50000.0 / 13.0) * viewingConditions.getNc() * viewingConditions.getNcb(); double p2 = (ac / viewingConditions.getNbb()); @@ -422,14 +377,11 @@ int viewed(ViewingConditions viewingConditions) { double bA = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0; double rCBase = max(0, (27.13 * Math.abs(rA)) / (400.0 - Math.abs(rA))); - double rC = - Math.signum(rA) * (100.0 / viewingConditions.getFl()) * Math.pow(rCBase, 1.0 / 0.42); + double rC = Math.signum(rA) * (100.0 / viewingConditions.getFl()) * Math.pow(rCBase, 1.0 / 0.42); double gCBase = max(0, (27.13 * Math.abs(gA)) / (400.0 - Math.abs(gA))); - double gC = - Math.signum(gA) * (100.0 / viewingConditions.getFl()) * Math.pow(gCBase, 1.0 / 0.42); + double gC = Math.signum(gA) * (100.0 / viewingConditions.getFl()) * Math.pow(gCBase, 1.0 / 0.42); double bCBase = max(0, (27.13 * Math.abs(bA)) / (400.0 - Math.abs(bA))); - double bC = - Math.signum(bA) * (100.0 / viewingConditions.getFl()) * Math.pow(bCBase, 1.0 / 0.42); + double bC = Math.signum(bA) * (100.0 / viewingConditions.getFl()) * Math.pow(bCBase, 1.0 / 0.42); double rF = rC / viewingConditions.getRgbD()[0]; double gF = gC / viewingConditions.getRgbD()[1]; double bF = bC / viewingConditions.getRgbD()[2]; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Hct.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Hct.java index 1f233c26..96cc62a6 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Hct.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/Hct.java @@ -136,15 +136,12 @@ public Hct inViewingConditions(ViewingConditions vc) { double[] viewedInVc = cam16.xyzInViewingConditions(vc, null); // 2. Create CAM16 of those XYZ coordinates in default VC. - Cam16 recastInVc = - Cam16.fromXyzInViewingConditions( - viewedInVc[0], viewedInVc[1], viewedInVc[2], ViewingConditions.DEFAULT); + Cam16 recastInVc = Cam16.fromXyzInViewingConditions(viewedInVc[0], viewedInVc[1], viewedInVc[2], ViewingConditions.DEFAULT); // 3. Create HCT from: // - CAM16 using default VC with XYZ coordinates in specified VC. // - L* converted from Y in XYZ coordinates in specified VC. - return Hct.from( - recastInVc.getHue(), recastInVc.getChroma(), ColorUtils.lstarFromY(viewedInVc[1])); + return Hct.from(recastInVc.getHue(), recastInVc.getChroma(), ColorUtils.lstarFromY(viewedInVc[1])); } private void setInternalState(int argb) { diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/HctSolver.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/HctSolver.java index 70fe3829..4590ec55 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/HctSolver.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/HctSolver.java @@ -25,289 +25,10 @@ * A class that solves the HCT equation. */ public class HctSolver { - static final double[][] SCALED_DISCOUNT_FROM_LINRGB = - new double[][]{ - new double[]{ - 0.001200833568784504, 0.002389694492170889, 0.0002795742885861124, - }, - new double[]{ - 0.0005891086651375999, 0.0029785502573438758, 0.0003270666104008398, - }, - new double[]{ - 0.00010146692491640572, 0.0005364214359186694, 0.0032979401770712076, - }, - }; - static final double[][] LINRGB_FROM_SCALED_DISCOUNT = - new double[][]{ - new double[]{ - 1373.2198709594231, -1100.4251190754821, -7.278681089101213, - }, - new double[]{ - -271.815969077903, 559.6580465940733, -32.46047482791194, - }, - new double[]{ - 1.9622899599665666, -57.173814538844006, 308.7233197812385, - }, - }; + static final double[][] SCALED_DISCOUNT_FROM_LINRGB = new double[][]{new double[]{0.001200833568784504, 0.002389694492170889, 0.0002795742885861124,}, new double[]{0.0005891086651375999, 0.0029785502573438758, 0.0003270666104008398,}, new double[]{0.00010146692491640572, 0.0005364214359186694, 0.0032979401770712076,},}; + static final double[][] LINRGB_FROM_SCALED_DISCOUNT = new double[][]{new double[]{1373.2198709594231, -1100.4251190754821, -7.278681089101213,}, new double[]{-271.815969077903, 559.6580465940733, -32.46047482791194,}, new double[]{1.9622899599665666, -57.173814538844006, 308.7233197812385,},}; static final double[] Y_FROM_LINRGB = new double[]{0.2126, 0.7152, 0.0722}; - static final double[] CRITICAL_PLANES = - new double[]{ - 0.015176349177441876, - 0.045529047532325624, - 0.07588174588720938, - 0.10623444424209313, - 0.13658714259697685, - 0.16693984095186062, - 0.19729253930674434, - 0.2276452376616281, - 0.2579979360165119, - 0.28835063437139563, - 0.3188300904430532, - 0.350925934958123, - 0.3848314933096426, - 0.42057480301049466, - 0.458183274052838, - 0.4976837250274023, - 0.5391024159806381, - 0.5824650784040898, - 0.6277969426914107, - 0.6751227633498623, - 0.7244668422128921, - 0.775853049866786, - 0.829304845476233, - 0.8848452951698498, - 0.942497089126609, - 1.0022825574869039, - 1.0642236851973577, - 1.1283421258858297, - 1.1946592148522128, - 1.2631959812511864, - 1.3339731595349034, - 1.407011200216447, - 1.4823302800086415, - 1.5599503113873272, - 1.6398909516233677, - 1.7221716113234105, - 1.8068114625156377, - 1.8938294463134073, - 1.9832442801866852, - 2.075074464868551, - 2.1693382909216234, - 2.2660538449872063, - 2.36523901573795, - 2.4669114995532007, - 2.5710888059345764, - 2.6777882626779785, - 2.7870270208169257, - 2.898822059350997, - 3.0131901897720907, - 3.1301480604002863, - 3.2497121605402226, - 3.3718988244681087, - 3.4967242352587946, - 3.624204428461639, - 3.754355295633311, - 3.887192587735158, - 4.022731918402185, - 4.160988767090289, - 4.301978482107941, - 4.445716283538092, - 4.592217266055746, - 4.741496401646282, - 4.893568542229298, - 5.048448422192488, - 5.20615066083972, - 5.3666897647573375, - 5.5300801301023865, - 5.696336044816294, - 5.865471690767354, - 6.037501145825082, - 6.212438385869475, - 6.390297286737924, - 6.571091626112461, - 6.7548350853498045, - 6.941541251256611, - 7.131223617812143, - 7.323895587840543, - 7.5195704746346665, - 7.7182615035334345, - 7.919981813454504, - 8.124744458384042, - 8.332562408825165, - 8.543448553206703, - 8.757415699253682, - 8.974476575321063, - 9.194643831691977, - 9.417930041841839, - 9.644347703669503, - 9.873909240696694, - 10.106627003236781, - 10.342513269534024, - 10.58158024687427, - 10.8238400726681, - 11.069304815507364, - 11.317986476196008, - 11.569896988756009, - 11.825048221409341, - 12.083451977536606, - 12.345119996613247, - 12.610063955123938, - 12.878295467455942, - 13.149826086772048, - 13.42466730586372, - 13.702830557985108, - 13.984327217668513, - 14.269168601521828, - 14.55736596900856, - 14.848930523210871, - 15.143873411576273, - 15.44220572664832, - 15.743938506781891, - 16.04908273684337, - 16.35764934889634, - 16.66964922287304, - 16.985093187232053, - 17.30399201960269, - 17.62635644741625, - 17.95219714852476, - 18.281524751807332, - 18.614349837764564, - 18.95068293910138, - 19.290534541298456, - 19.633915083172692, - 19.98083495742689, - 20.331304511189067, - 20.685334046541502, - 21.042933821039977, - 21.404114048223256, - 21.76888489811322, - 22.137256497705877, - 22.50923893145328, - 22.884842241736916, - 23.264076429332462, - 23.6469514538663, - 24.033477234264016, - 24.42366364919083, - 24.817520537484558, - 25.21505769858089, - 25.61628489293138, - 26.021211842414342, - 26.429848230738664, - 26.842203703840827, - 27.258287870275353, - 27.678110301598522, - 28.10168053274597, - 28.529008062403893, - 28.96010235337422, - 29.39497283293396, - 29.83362889318845, - 30.276079891419332, - 30.722335150426627, - 31.172403958865512, - 31.62629557157785, - 32.08401920991837, - 32.54558406207592, - 33.010999283389665, - 33.4802739966603, - 33.953417292456834, - 34.430438229418264, - 34.911345834551085, - 35.39614910352207, - 35.88485700094671, - 36.37747846067349, - 36.87402238606382, - 37.37449765026789, - 37.87891309649659, - 38.38727753828926, - 38.89959975977785, - 39.41588851594697, - 39.93615253289054, - 40.460400508064545, - 40.98864111053629, - 41.520882981230194, - 42.05713473317016, - 42.597404951718396, - 43.141702194811224, - 43.6900349931913, - 44.24241185063697, - 44.798841244188324, - 45.35933162437017, - 45.92389141541209, - 46.49252901546552, - 47.065252796817916, - 47.64207110610409, - 48.22299226451468, - 48.808024568002054, - 49.3971762874833, - 49.9904556690408, - 50.587870934119984, - 51.189430279724725, - 51.79514187861014, - 52.40501387947288, - 53.0190544071392, - 53.637271562750364, - 54.259673423945976, - 54.88626804504493, - 55.517063457223934, - 56.15206766869424, - 56.79128866487574, - 57.43473440856916, - 58.08241284012621, - 58.734331877617365, - 59.39049941699807, - 60.05092333227251, - 60.715611475655585, - 61.38457167773311, - 62.057811747619894, - 62.7353394731159, - 63.417162620860914, - 64.10328893648692, - 64.79372614476921, - 65.48848194977529, - 66.18756403501224, - 66.89098006357258, - 67.59873767827808, - 68.31084450182222, - 69.02730813691093, - 69.74813616640164, - 70.47333615344107, - 71.20291564160104, - 71.93688215501312, - 72.67524319850172, - 73.41800625771542, - 74.16517879925733, - 74.9167682708136, - 75.67278210128072, - 76.43322770089146, - 77.1981124613393, - 77.96744375590167, - 78.74122893956174, - 79.51947534912904, - 80.30219030335869, - 81.08938110306934, - 81.88105503125999, - 82.67721935322541, - 83.4778813166706, - 84.28304815182372, - 85.09272707154808, - 85.90692527145302, - 86.72564993000343, - 87.54890820862819, - 88.3767072518277, - 89.2090541872801, - 90.04595612594655, - 90.88742016217518, - 91.73345337380438, - 92.58406282226491, - 93.43925555268066, - 94.29903859396902, - 95.16341895893969, - 96.03240364439274, - 96.9059996312159, - 97.78421388448044, - 98.6670533535366, - 99.55452497210776, - }; + static final double[] CRITICAL_PLANES = new double[]{0.015176349177441876, 0.045529047532325624, 0.07588174588720938, 0.10623444424209313, 0.13658714259697685, 0.16693984095186062, 0.19729253930674434, 0.2276452376616281, 0.2579979360165119, 0.28835063437139563, 0.3188300904430532, 0.350925934958123, 0.3848314933096426, 0.42057480301049466, 0.458183274052838, 0.4976837250274023, 0.5391024159806381, 0.5824650784040898, 0.6277969426914107, 0.6751227633498623, 0.7244668422128921, 0.775853049866786, 0.829304845476233, 0.8848452951698498, 0.942497089126609, 1.0022825574869039, 1.0642236851973577, 1.1283421258858297, 1.1946592148522128, 1.2631959812511864, 1.3339731595349034, 1.407011200216447, 1.4823302800086415, 1.5599503113873272, 1.6398909516233677, 1.7221716113234105, 1.8068114625156377, 1.8938294463134073, 1.9832442801866852, 2.075074464868551, 2.1693382909216234, 2.2660538449872063, 2.36523901573795, 2.4669114995532007, 2.5710888059345764, 2.6777882626779785, 2.7870270208169257, 2.898822059350997, 3.0131901897720907, 3.1301480604002863, 3.2497121605402226, 3.3718988244681087, 3.4967242352587946, 3.624204428461639, 3.754355295633311, 3.887192587735158, 4.022731918402185, 4.160988767090289, 4.301978482107941, 4.445716283538092, 4.592217266055746, 4.741496401646282, 4.893568542229298, 5.048448422192488, 5.20615066083972, 5.3666897647573375, 5.5300801301023865, 5.696336044816294, 5.865471690767354, 6.037501145825082, 6.212438385869475, 6.390297286737924, 6.571091626112461, 6.7548350853498045, 6.941541251256611, 7.131223617812143, 7.323895587840543, 7.5195704746346665, 7.7182615035334345, 7.919981813454504, 8.124744458384042, 8.332562408825165, 8.543448553206703, 8.757415699253682, 8.974476575321063, 9.194643831691977, 9.417930041841839, 9.644347703669503, 9.873909240696694, 10.106627003236781, 10.342513269534024, 10.58158024687427, 10.8238400726681, 11.069304815507364, 11.317986476196008, 11.569896988756009, 11.825048221409341, 12.083451977536606, 12.345119996613247, 12.610063955123938, 12.878295467455942, 13.149826086772048, 13.42466730586372, 13.702830557985108, 13.984327217668513, 14.269168601521828, 14.55736596900856, 14.848930523210871, 15.143873411576273, 15.44220572664832, 15.743938506781891, 16.04908273684337, 16.35764934889634, 16.66964922287304, 16.985093187232053, 17.30399201960269, 17.62635644741625, 17.95219714852476, 18.281524751807332, 18.614349837764564, 18.95068293910138, 19.290534541298456, 19.633915083172692, 19.98083495742689, 20.331304511189067, 20.685334046541502, 21.042933821039977, 21.404114048223256, 21.76888489811322, 22.137256497705877, 22.50923893145328, 22.884842241736916, 23.264076429332462, 23.6469514538663, 24.033477234264016, 24.42366364919083, 24.817520537484558, 25.21505769858089, 25.61628489293138, 26.021211842414342, 26.429848230738664, 26.842203703840827, 27.258287870275353, 27.678110301598522, 28.10168053274597, 28.529008062403893, 28.96010235337422, 29.39497283293396, 29.83362889318845, 30.276079891419332, 30.722335150426627, 31.172403958865512, 31.62629557157785, 32.08401920991837, 32.54558406207592, 33.010999283389665, 33.4802739966603, 33.953417292456834, 34.430438229418264, 34.911345834551085, 35.39614910352207, 35.88485700094671, 36.37747846067349, 36.87402238606382, 37.37449765026789, 37.87891309649659, 38.38727753828926, 38.89959975977785, 39.41588851594697, 39.93615253289054, 40.460400508064545, 40.98864111053629, 41.520882981230194, 42.05713473317016, 42.597404951718396, 43.141702194811224, 43.6900349931913, 44.24241185063697, 44.798841244188324, 45.35933162437017, 45.92389141541209, 46.49252901546552, 47.065252796817916, 47.64207110610409, 48.22299226451468, 48.808024568002054, 49.3971762874833, 49.9904556690408, 50.587870934119984, 51.189430279724725, 51.79514187861014, 52.40501387947288, 53.0190544071392, 53.637271562750364, 54.259673423945976, 54.88626804504493, 55.517063457223934, 56.15206766869424, 56.79128866487574, 57.43473440856916, 58.08241284012621, 58.734331877617365, 59.39049941699807, 60.05092333227251, 60.715611475655585, 61.38457167773311, 62.057811747619894, 62.7353394731159, 63.417162620860914, 64.10328893648692, 64.79372614476921, 65.48848194977529, 66.18756403501224, 66.89098006357258, 67.59873767827808, 68.31084450182222, 69.02730813691093, 69.74813616640164, 70.47333615344107, 71.20291564160104, 71.93688215501312, 72.67524319850172, 73.41800625771542, 74.16517879925733, 74.9167682708136, 75.67278210128072, 76.43322770089146, 77.1981124613393, 77.96744375590167, 78.74122893956174, 79.51947534912904, 80.30219030335869, 81.08938110306934, 81.88105503125999, 82.67721935322541, 83.4778813166706, 84.28304815182372, 85.09272707154808, 85.90692527145302, 86.72564993000343, 87.54890820862819, 88.3767072518277, 89.2090541872801, 90.04595612594655, 90.88742016217518, 91.73345337380438, 92.58406282226491, 93.43925555268066, 94.29903859396902, 95.16341895893969, 96.03240364439274, 96.9059996312159, 97.78421388448044, 98.6670533535366, 99.55452497210776,}; private HctSolver() { } @@ -381,11 +102,7 @@ static double intercept(double source, double mid, double target) { } static double[] lerpPoint(double[] source, double t, double[] target) { - return new double[]{ - source[0] + (target[0] - source[0]) * t, - source[1] + (target[1] - source[1]) * t, - source[2] + (target[2] - source[2]) * t, - }; + return new double[]{source[0] + (target[0] - source[0]) * t, source[1] + (target[1] - source[1]) * t, source[2] + (target[2] - source[2]) * t,}; } /** @@ -496,9 +213,7 @@ static double[][] bisectToSegment(double y, double targetHue) { } static double[] midpoint(double[] a, double[] b) { - return new double[]{ - (a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2, - }; + return new double[]{(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2,}; } static int criticalPlaneBelow(double x) { @@ -588,9 +303,7 @@ static int findResultByJ(double hueRadians, double chroma, double y) { double jNormalized = j / 100.0; double alpha = chroma == 0.0 || j == 0.0 ? 0.0 : chroma / Math.sqrt(jNormalized); double t = Math.pow(alpha * tInnerCoeff, 1.0 / 0.9); - double ac = - viewingConditions.getAw() - * Math.pow(jNormalized, 1.0 / viewingConditions.getC() / viewingConditions.getZ()); + double ac = viewingConditions.getAw() * Math.pow(jNormalized, 1.0 / viewingConditions.getC() / viewingConditions.getZ()); double p2 = ac / viewingConditions.getNbb(); double gamma = 23.0 * (p2 + 0.305) * t / (23.0 * p1 + 11 * t * hCos + 108.0 * t * hSin); double a = gamma * hCos; @@ -601,9 +314,7 @@ static int findResultByJ(double hueRadians, double chroma, double y) { double rCScaled = inverseChromaticAdaptation(rA); double gCScaled = inverseChromaticAdaptation(gA); double bCScaled = inverseChromaticAdaptation(bA); - double[] linrgb = - MathUtils.matrixMultiply( - new double[]{rCScaled, gCScaled, bCScaled}, LINRGB_FROM_SCALED_DISCOUNT); + double[] linrgb = MathUtils.matrixMultiply(new double[]{rCScaled, gCScaled, bCScaled}, LINRGB_FROM_SCALED_DISCOUNT); // =========================================================== // Operations inlined from Cam16 to avoid repeated calculation // =========================================================== diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/ViewingConditions.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/ViewingConditions.java index 17f8943e..942a0a6c 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/ViewingConditions.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/hct/ViewingConditions.java @@ -34,8 +34,7 @@ public final class ViewingConditions { /** * sRGB-like viewing conditions. */ - public static final ViewingConditions DEFAULT = - ViewingConditions.defaultWithBackgroundLstar(50.0); + public static final ViewingConditions DEFAULT = ViewingConditions.defaultWithBackgroundLstar(50.0); private final double aw; private final double nbb; @@ -54,17 +53,7 @@ public final class ViewingConditions { * individually. A brief overview is available in the CAM16 specification, and a complete overview * requires a color science textbook, such as Fairchild's Color Appearance Models. */ - private ViewingConditions( - double n, - double aw, - double nbb, - double ncb, - double c, - double nc, - double[] rgbD, - double fl, - double flRoot, - double z) { + private ViewingConditions(double n, double aw, double nbb, double ncb, double c, double nc, double[] rgbD, double fl, double flRoot, double z) { this.n = n; this.aw = aw; this.nbb = nbb; @@ -95,12 +84,7 @@ private ViewingConditions( * such as knowing an apple is still red in green light. default = false, the eye does not * perform this process on self-luminous objects like displays. */ - public static ViewingConditions make( - double[] whitePoint, - double adaptingLuminance, - double backgroundLstar, - double surround, - boolean discountingIlluminant) { + public static ViewingConditions make(double[] whitePoint, double adaptingLuminance, double backgroundLstar, double surround, boolean discountingIlluminant) { // A background of pure black is non-physical and leads to infinities that represent the idea // that any color viewed in pure black can't be seen. backgroundLstar = Math.max(0.1, backgroundLstar); @@ -111,20 +95,11 @@ public static ViewingConditions make( double gW = (xyz[0] * matrix[1][0]) + (xyz[1] * matrix[1][1]) + (xyz[2] * matrix[1][2]); double bW = (xyz[0] * matrix[2][0]) + (xyz[1] * matrix[2][1]) + (xyz[2] * matrix[2][2]); double f = 0.8 + (surround / 10.0); - double c = - (f >= 0.9) - ? MathUtils.lerp(0.59, 0.69, ((f - 0.9) * 10.0)) - : MathUtils.lerp(0.525, 0.59, ((f - 0.8) * 10.0)); - double d = - discountingIlluminant - ? 1.0 - : f * (1.0 - ((1.0 / 3.6) * Math.exp((-adaptingLuminance - 42.0) / 92.0))); + double c = (f >= 0.9) ? MathUtils.lerp(0.59, 0.69, ((f - 0.9) * 10.0)) : MathUtils.lerp(0.525, 0.59, ((f - 0.8) * 10.0)); + double d = discountingIlluminant ? 1.0 : f * (1.0 - ((1.0 / 3.6) * Math.exp((-adaptingLuminance - 42.0) / 92.0))); d = MathUtils.clampDouble(0.0, 1.0, d); double nc = f; - double[] rgbD = - new double[]{ - d * (100.0 / rW) + 1.0 - d, d * (100.0 / gW) + 1.0 - d, d * (100.0 / bW) + 1.0 - d - }; + double[] rgbD = new double[]{d * (100.0 / rW) + 1.0 - d, d * (100.0 / gW) + 1.0 - d, d * (100.0 / bW) + 1.0 - d}; double k = 1.0 / (5.0 * adaptingLuminance + 1.0); double k4 = k * k * k * k; double k4F = 1.0 - k4; @@ -133,19 +108,9 @@ public static ViewingConditions make( double z = 1.48 + Math.sqrt(n); double nbb = 0.725 / Math.pow(n, 0.2); double ncb = nbb; - double[] rgbAFactors = - new double[]{ - Math.pow(fl * rgbD[0] * rW / 100.0, 0.42), - Math.pow(fl * rgbD[1] * gW / 100.0, 0.42), - Math.pow(fl * rgbD[2] * bW / 100.0, 0.42) - }; - - double[] rgbA = - new double[]{ - (400.0 * rgbAFactors[0]) / (rgbAFactors[0] + 27.13), - (400.0 * rgbAFactors[1]) / (rgbAFactors[1] + 27.13), - (400.0 * rgbAFactors[2]) / (rgbAFactors[2] + 27.13) - }; + double[] rgbAFactors = new double[]{Math.pow(fl * rgbD[0] * rW / 100.0, 0.42), Math.pow(fl * rgbD[1] * gW / 100.0, 0.42), Math.pow(fl * rgbD[2] * bW / 100.0, 0.42)}; + + double[] rgbA = new double[]{(400.0 * rgbAFactors[0]) / (rgbAFactors[0] + 27.13), (400.0 * rgbAFactors[1]) / (rgbAFactors[1] + 27.13), (400.0 * rgbAFactors[2]) / (rgbAFactors[2] + 27.13)}; double aw = ((2.0 * rgbA[0]) + rgbA[1] + (0.05 * rgbA[2])) * nbb; return new ViewingConditions(n, aw, nbb, ncb, c, nc, rgbD, fl, Math.pow(fl, 0.25), z); @@ -157,12 +122,7 @@ public static ViewingConditions make( *

Default viewing conditions have a lstar of 50, midgray. */ public static ViewingConditions defaultWithBackgroundLstar(double lstar) { - return ViewingConditions.make( - ColorUtils.whitePointD65(), - (200.0 / Math.PI * ColorUtils.yFromLstar(50.0) / 100.f), - lstar, - 2.0, - false); + return ViewingConditions.make(ColorUtils.whitePointD65(), (200.0 / Math.PI * ColorUtils.yFromLstar(50.0) / 100.f), lstar, 2.0, false); } public double getAw() { diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/palettes/TonalPalette.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/palettes/TonalPalette.java index 9125d0e2..595924c8 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/palettes/TonalPalette.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/palettes/TonalPalette.java @@ -65,47 +65,8 @@ public static TonalPalette fromHct(Hct hct) { * @return Tones matching hue and chroma. */ public static TonalPalette fromHueAndChroma(double hue, double chroma) { - return new TonalPalette(hue, chroma, createKeyColor(hue, chroma)); - } - - /** - * The key color is the first tone, starting from T50, matching the given hue and chroma. - */ - private static Hct createKeyColor(double hue, double chroma) { - double startTone = 50.0; - Hct smallestDeltaHct = Hct.from(hue, chroma, startTone); - double smallestDelta = Math.abs(smallestDeltaHct.getChroma() - chroma); - // Starting from T50, check T+/-delta to see if they match the requested - // chroma. - // - // Starts from T50 because T50 has the most chroma available, on - // average. Thus it is most likely to have a direct answer and minimize - // iteration. - for (double delta = 1.0; delta < 50.0; delta += 1.0) { - // Termination condition rounding instead of minimizing delta to avoid - // case where requested chroma is 16.51, and the closest chroma is 16.49. - // Error is minimized, but when rounded and displayed, requested chroma - // is 17, key color's chroma is 16. - if (Math.round(chroma) == Math.round(smallestDeltaHct.getChroma())) { - return smallestDeltaHct; - } - - final Hct hctAdd = Hct.from(hue, chroma, startTone + delta); - final double hctAddDelta = Math.abs(hctAdd.getChroma() - chroma); - if (hctAddDelta < smallestDelta) { - smallestDelta = hctAddDelta; - smallestDeltaHct = hctAdd; - } - - final Hct hctSubtract = Hct.from(hue, chroma, startTone - delta); - final double hctSubtractDelta = Math.abs(hctSubtract.getChroma() - chroma); - if (hctSubtractDelta < smallestDelta) { - smallestDelta = hctSubtractDelta; - smallestDeltaHct = hctSubtract; - } - } - - return smallestDeltaHct; + final Hct keyColor = new KeyColor(hue, chroma).create(); + return new TonalPalette(hue, chroma, keyColor); } /** @@ -152,4 +113,77 @@ public double getHue() { public Hct getKeyColor() { return this.keyColor; } -} + + /** + * Key color is a color that represents the hue and chroma of a tonal palette. + */ + private static final class KeyColor { + private static final double MAX_CHROMA_VALUE = 200.0; + private final double hue; + private final double requestedChroma; + // Cache that maps tone to max chroma to avoid duplicated HCT calculation. + private final Map chromaCache = new HashMap<>(); + + /** + * Key color is a color that represents the hue and chroma of a tonal palette + */ + public KeyColor(double hue, double requestedChroma) { + this.hue = hue; + this.requestedChroma = requestedChroma; + } + + /** + * Creates a key color from a [hue] and a [chroma]. The key color is the first tone, starting + * from T50, matching the given hue and chroma. + * + * @return Key color [Hct] + */ + public Hct create() { + // Pivot around T50 because T50 has the most chroma available, on + // average. Thus it is most likely to have a direct answer. + final int pivotTone = 50; + final int toneStepSize = 1; + // Epsilon to accept values slightly higher than the requested chroma. + final double epsilon = 0.01; + + // Binary search to find the tone that can provide a chroma that is closest + // to the requested chroma. + int lowerTone = 0; + int upperTone = 100; + while (lowerTone < upperTone) { + final int midTone = (lowerTone + upperTone) / 2; + boolean isAscending = maxChroma(midTone) < maxChroma(midTone + toneStepSize); + boolean sufficientChroma = maxChroma(midTone) >= requestedChroma - epsilon; + + if (sufficientChroma) { + // Either range [lowerTone, midTone] or [midTone, upperTone] has + // the answer, so search in the range that is closer the pivot tone. + if (Math.abs(lowerTone - pivotTone) < Math.abs(upperTone - pivotTone)) { + upperTone = midTone; + } else { + if (lowerTone == midTone) { + return Hct.from(this.hue, this.requestedChroma, lowerTone); + } + lowerTone = midTone; + } + } else { + // As there is no sufficient chroma in the midTone, follow the direction to the chroma + // peak. + if (isAscending) { + lowerTone = midTone + toneStepSize; + } else { + // Keep midTone for potential chroma peak. + upperTone = midTone; + } + } + } + + return Hct.from(this.hue, this.requestedChroma, lowerTone); + } + + // Find the maximum chroma for a given tone + private double maxChroma(int tone) { + return chromaCache.computeIfAbsent(tone, (Integer key) -> Hct.from(hue, MAX_CHROMA_VALUE, key).getChroma()); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWsmeans.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWsmeans.java index e548dfba..2b295d0a 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWsmeans.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWsmeans.java @@ -54,8 +54,7 @@ private QuantizerWsmeans() { * @return Map with keys of colors in ARGB format, values of how many of the input pixels belong * to the color. */ - public static Map quantize( - int[] inputPixels, int[] startingClusters, int maxColors) { + public static Map quantize(int[] inputPixels, int[] startingClusters, int maxColors) { // Uses a seeded random number generator to ensure consistent results. Random random = new Random(0x42688); @@ -158,8 +157,7 @@ public static Map quantize( } } if (newClusterIndex != -1) { - double distanceChange = - Math.abs(Math.sqrt(minimumDistance) - Math.sqrt(previousDistance)); + double distanceChange = Math.abs(Math.sqrt(minimumDistance) - Math.sqrt(previousDistance)); if (distanceChange > MIN_MOVEMENT_DISTANCE) { pointsMoved++; clusterIndices[i] = newClusterIndex; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWu.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWu.java index fc11e1b2..759269fd 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWu.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/quantize/QuantizerWu.java @@ -49,33 +49,17 @@ static int getIndex(int r, int g, int b) { } static int volume(Box cube, int[] moment) { - return (moment[getIndex(cube.r1, cube.g1, cube.b1)] - - moment[getIndex(cube.r1, cube.g1, cube.b0)] - - moment[getIndex(cube.r1, cube.g0, cube.b1)] - + moment[getIndex(cube.r1, cube.g0, cube.b0)] - - moment[getIndex(cube.r0, cube.g1, cube.b1)] - + moment[getIndex(cube.r0, cube.g1, cube.b0)] - + moment[getIndex(cube.r0, cube.g0, cube.b1)] - - moment[getIndex(cube.r0, cube.g0, cube.b0)]); + return (moment[getIndex(cube.r1, cube.g1, cube.b1)] - moment[getIndex(cube.r1, cube.g1, cube.b0)] - moment[getIndex(cube.r1, cube.g0, cube.b1)] + moment[getIndex(cube.r1, cube.g0, cube.b0)] - moment[getIndex(cube.r0, cube.g1, cube.b1)] + moment[getIndex(cube.r0, cube.g1, cube.b0)] + moment[getIndex(cube.r0, cube.g0, cube.b1)] - moment[getIndex(cube.r0, cube.g0, cube.b0)]); } static int bottom(Box cube, Direction direction, int[] moment) { switch (direction) { case RED: - return -moment[getIndex(cube.r0, cube.g1, cube.b1)] - + moment[getIndex(cube.r0, cube.g1, cube.b0)] - + moment[getIndex(cube.r0, cube.g0, cube.b1)] - - moment[getIndex(cube.r0, cube.g0, cube.b0)]; + return -moment[getIndex(cube.r0, cube.g1, cube.b1)] + moment[getIndex(cube.r0, cube.g1, cube.b0)] + moment[getIndex(cube.r0, cube.g0, cube.b1)] - moment[getIndex(cube.r0, cube.g0, cube.b0)]; case GREEN: - return -moment[getIndex(cube.r1, cube.g0, cube.b1)] - + moment[getIndex(cube.r1, cube.g0, cube.b0)] - + moment[getIndex(cube.r0, cube.g0, cube.b1)] - - moment[getIndex(cube.r0, cube.g0, cube.b0)]; + return -moment[getIndex(cube.r1, cube.g0, cube.b1)] + moment[getIndex(cube.r1, cube.g0, cube.b0)] + moment[getIndex(cube.r0, cube.g0, cube.b1)] - moment[getIndex(cube.r0, cube.g0, cube.b0)]; case BLUE: - return -moment[getIndex(cube.r1, cube.g1, cube.b0)] - + moment[getIndex(cube.r1, cube.g0, cube.b0)] - + moment[getIndex(cube.r0, cube.g1, cube.b0)] - - moment[getIndex(cube.r0, cube.g0, cube.b0)]; + return -moment[getIndex(cube.r1, cube.g1, cube.b0)] + moment[getIndex(cube.r1, cube.g0, cube.b0)] + moment[getIndex(cube.r0, cube.g1, cube.b0)] - moment[getIndex(cube.r0, cube.g0, cube.b0)]; } throw new IllegalArgumentException("unexpected direction " + direction); } @@ -83,20 +67,11 @@ static int bottom(Box cube, Direction direction, int[] moment) { static int top(Box cube, Direction direction, int position, int[] moment) { switch (direction) { case RED: - return (moment[getIndex(position, cube.g1, cube.b1)] - - moment[getIndex(position, cube.g1, cube.b0)] - - moment[getIndex(position, cube.g0, cube.b1)] - + moment[getIndex(position, cube.g0, cube.b0)]); + return (moment[getIndex(position, cube.g1, cube.b1)] - moment[getIndex(position, cube.g1, cube.b0)] - moment[getIndex(position, cube.g0, cube.b1)] + moment[getIndex(position, cube.g0, cube.b0)]); case GREEN: - return (moment[getIndex(cube.r1, position, cube.b1)] - - moment[getIndex(cube.r1, position, cube.b0)] - - moment[getIndex(cube.r0, position, cube.b1)] - + moment[getIndex(cube.r0, position, cube.b0)]); + return (moment[getIndex(cube.r1, position, cube.b1)] - moment[getIndex(cube.r1, position, cube.b0)] - moment[getIndex(cube.r0, position, cube.b1)] + moment[getIndex(cube.r0, position, cube.b0)]); case BLUE: - return (moment[getIndex(cube.r1, cube.g1, position)] - - moment[getIndex(cube.r1, cube.g0, position)] - - moment[getIndex(cube.r0, cube.g1, position)] - + moment[getIndex(cube.r0, cube.g0, position)]); + return (moment[getIndex(cube.r1, cube.g1, position)] - moment[getIndex(cube.r1, cube.g0, position)] - moment[getIndex(cube.r0, cube.g1, position)] + moment[getIndex(cube.r0, cube.g0, position)]); } throw new IllegalArgumentException("unexpected direction " + direction); } @@ -240,15 +215,7 @@ List createResult(int colorCount) { int dr = volume(cube, momentsR); int dg = volume(cube, momentsG); int db = volume(cube, momentsB); - double xx = - moments[getIndex(cube.r1, cube.g1, cube.b1)] - - moments[getIndex(cube.r1, cube.g1, cube.b0)] - - moments[getIndex(cube.r1, cube.g0, cube.b1)] - + moments[getIndex(cube.r1, cube.g0, cube.b0)] - - moments[getIndex(cube.r0, cube.g1, cube.b1)] - + moments[getIndex(cube.r0, cube.g1, cube.b0)] - + moments[getIndex(cube.r0, cube.g0, cube.b1)] - - moments[getIndex(cube.r0, cube.g0, cube.b0)]; + double xx = moments[getIndex(cube.r1, cube.g1, cube.b1)] - moments[getIndex(cube.r1, cube.g1, cube.b0)] - moments[getIndex(cube.r1, cube.g0, cube.b1)] + moments[getIndex(cube.r1, cube.g0, cube.b0)] - moments[getIndex(cube.r0, cube.g1, cube.b1)] + moments[getIndex(cube.r0, cube.g1, cube.b0)] + moments[getIndex(cube.r0, cube.g0, cube.b1)] - moments[getIndex(cube.r0, cube.g0, cube.b0)]; int hypotenuse = dr * dr + dg * dg + db * db; int volume = volume(cube, weights); @@ -261,12 +228,9 @@ Boolean cut(Box one, Box two) { int wholeB = volume(one, momentsB); int wholeW = volume(one, weights); - MaximizeResult maxRResult = - maximize(one, Direction.RED, one.r0 + 1, one.r1, wholeR, wholeG, wholeB, wholeW); - MaximizeResult maxGResult = - maximize(one, Direction.GREEN, one.g0 + 1, one.g1, wholeR, wholeG, wholeB, wholeW); - MaximizeResult maxBResult = - maximize(one, Direction.BLUE, one.b0 + 1, one.b1, wholeR, wholeG, wholeB, wholeW); + MaximizeResult maxRResult = maximize(one, Direction.RED, one.r0 + 1, one.r1, wholeR, wholeG, wholeB, wholeW); + MaximizeResult maxGResult = maximize(one, Direction.GREEN, one.g0 + 1, one.g1, wholeR, wholeG, wholeB, wholeW); + MaximizeResult maxBResult = maximize(one, Direction.BLUE, one.b0 + 1, one.b1, wholeR, wholeG, wholeB, wholeW); Direction cutDirection; double maxR = maxRResult.maximum; double maxG = maxGResult.maximum; @@ -313,15 +277,7 @@ Boolean cut(Box one, Box two) { return true; } - MaximizeResult maximize( - Box cube, - Direction direction, - int first, - int last, - int wholeR, - int wholeG, - int wholeB, - int wholeW) { + MaximizeResult maximize(Box cube, Direction direction, int first, int last, int wholeR, int wholeG, int wholeB, int wholeW) { int bottomR = bottom(cube, direction, momentsR); int bottomG = bottom(cube, direction, momentsG); int bottomB = bottom(cube, direction, momentsB); @@ -368,9 +324,7 @@ MaximizeResult maximize( } private enum Direction { - RED, - GREEN, - BLUE + RED, GREEN, BLUE } private static final class MaximizeResult { diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/DynamicScheme.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/DynamicScheme.java deleted file mode 100644 index af55c8a4..00000000 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/DynamicScheme.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.drdisagree.colorblendr.utils.monet.scheme; - -import com.drdisagree.colorblendr.utils.monet.hct.Hct; -import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; -import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; - -/** - * Provides important settings for creating colors dynamically, and 6 color palettes. Requires: 1. A - * color. (source color) 2. A theme. (Variant) 3. Whether or not its dark mode. 4. Contrast level. - * (-1 to 1, currently contrast ratio 3.0 and 7.0) - */ -public class DynamicScheme { - public final int sourceColorArgb; - public final Hct sourceColorHct; - public final Variant variant; - public final boolean isDark; - public final double contrastLevel; - - public final TonalPalette primaryPalette; - public final TonalPalette secondaryPalette; - public final TonalPalette tertiaryPalette; - public final TonalPalette neutralPalette; - public final TonalPalette neutralVariantPalette; - public final TonalPalette errorPalette; - - public DynamicScheme( - Hct sourceColorHct, - Variant variant, - boolean isDark, - double contrastLevel, - TonalPalette primaryPalette, - TonalPalette secondaryPalette, - TonalPalette tertiaryPalette, - TonalPalette neutralPalette, - TonalPalette neutralVariantPalette) { - this.sourceColorArgb = sourceColorHct.toInt(); - this.sourceColorHct = sourceColorHct; - this.variant = variant; - this.isDark = isDark; - this.contrastLevel = contrastLevel; - - this.primaryPalette = primaryPalette; - this.secondaryPalette = secondaryPalette; - this.tertiaryPalette = tertiaryPalette; - this.neutralPalette = neutralPalette; - this.neutralVariantPalette = neutralVariantPalette; - this.errorPalette = TonalPalette.fromHueAndChroma(25.0, 84.0); - } - - /** - * Given a set of hues and set of hue rotations, locate which hues the source color's hue is - * between, apply the rotation at the same index as the first hue in the range, and return the - * rotated hue. - * - * @param sourceColorHct The color whose hue should be rotated. - * @param hues A set of hues. - * @param rotations A set of hue rotations. - * @return Color's hue with a rotation applied. - */ - public static double getRotatedHue(Hct sourceColorHct, double[] hues, double[] rotations) { - final double sourceHue = sourceColorHct.getHue(); - if (rotations.length == 1) { - return MathUtils.sanitizeDegreesDouble(sourceHue + rotations[0]); - } - final int size = hues.length; - for (int i = 0; i <= (size - 2); i++) { - final double thisHue = hues[i]; - final double nextHue = hues[i + 1]; - if (thisHue < sourceHue && sourceHue < nextHue) { - return MathUtils.sanitizeDegreesDouble(sourceHue + rotations[i]); - } - } - // If this statement executes, something is wrong, there should have been a rotation - // found using the arrays. - return sourceHue; - } -} diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Scheme.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Scheme.java index 56197283..60f4cfbd 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Scheme.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/Scheme.java @@ -18,11 +18,18 @@ package com.drdisagree.colorblendr.utils.monet.scheme; +import androidx.annotation.NonNull; + import com.drdisagree.colorblendr.utils.monet.palettes.CorePalette; /** * Represents a Material color scheme, a mapping of color roles to colors. + * + * @deprecated Please use dynamiccolor.DynamicScheme instead. Refer + * to ... + * for migration guidance. */ +@Deprecated public class Scheme { private int primary; private int onPrimary; @@ -57,36 +64,7 @@ public class Scheme { public Scheme() { } - public Scheme( - int primary, - int onPrimary, - int primaryContainer, - int onPrimaryContainer, - int secondary, - int onSecondary, - int secondaryContainer, - int onSecondaryContainer, - int tertiary, - int onTertiary, - int tertiaryContainer, - int onTertiaryContainer, - int error, - int onError, - int errorContainer, - int onErrorContainer, - int background, - int onBackground, - int surface, - int onSurface, - int surfaceVariant, - int onSurfaceVariant, - int outline, - int outlineVariant, - int shadow, - int scrim, - int inverseSurface, - int inverseOnSurface, - int inversePrimary) { + public Scheme(int primary, int onPrimary, int primaryContainer, int onPrimaryContainer, int secondary, int onSecondary, int secondaryContainer, int onSecondaryContainer, int tertiary, int onTertiary, int tertiaryContainer, int onTertiaryContainer, int error, int onError, int errorContainer, int onErrorContainer, int background, int onBackground, int surface, int onSurface, int surfaceVariant, int onSurfaceVariant, int outline, int outlineVariant, int shadow, int scrim, int inverseSurface, int inverseOnSurface, int inversePrimary) { super(); this.primary = primary; this.onPrimary = onPrimary; @@ -148,69 +126,11 @@ public static Scheme darkContent(int argb) { } private static Scheme lightFromCorePalette(CorePalette core) { - return new Scheme() - .withPrimary(core.a1.tone(40)) - .withOnPrimary(core.a1.tone(100)) - .withPrimaryContainer(core.a1.tone(90)) - .withOnPrimaryContainer(core.a1.tone(10)) - .withSecondary(core.a2.tone(40)) - .withOnSecondary(core.a2.tone(100)) - .withSecondaryContainer(core.a2.tone(90)) - .withOnSecondaryContainer(core.a2.tone(10)) - .withTertiary(core.a3.tone(40)) - .withOnTertiary(core.a3.tone(100)) - .withTertiaryContainer(core.a3.tone(90)) - .withOnTertiaryContainer(core.a3.tone(10)) - .withError(core.error.tone(40)) - .withOnError(core.error.tone(100)) - .withErrorContainer(core.error.tone(90)) - .withOnErrorContainer(core.error.tone(10)) - .withBackground(core.n1.tone(99)) - .withOnBackground(core.n1.tone(10)) - .withSurface(core.n1.tone(99)) - .withOnSurface(core.n1.tone(10)) - .withSurfaceVariant(core.n2.tone(90)) - .withOnSurfaceVariant(core.n2.tone(30)) - .withOutline(core.n2.tone(50)) - .withOutlineVariant(core.n2.tone(80)) - .withShadow(core.n1.tone(0)) - .withScrim(core.n1.tone(0)) - .withInverseSurface(core.n1.tone(20)) - .withInverseOnSurface(core.n1.tone(95)) - .withInversePrimary(core.a1.tone(80)); + return new Scheme().withPrimary(core.a1.tone(40)).withOnPrimary(core.a1.tone(100)).withPrimaryContainer(core.a1.tone(90)).withOnPrimaryContainer(core.a1.tone(10)).withSecondary(core.a2.tone(40)).withOnSecondary(core.a2.tone(100)).withSecondaryContainer(core.a2.tone(90)).withOnSecondaryContainer(core.a2.tone(10)).withTertiary(core.a3.tone(40)).withOnTertiary(core.a3.tone(100)).withTertiaryContainer(core.a3.tone(90)).withOnTertiaryContainer(core.a3.tone(10)).withError(core.error.tone(40)).withOnError(core.error.tone(100)).withErrorContainer(core.error.tone(90)).withOnErrorContainer(core.error.tone(10)).withBackground(core.n1.tone(99)).withOnBackground(core.n1.tone(10)).withSurface(core.n1.tone(99)).withOnSurface(core.n1.tone(10)).withSurfaceVariant(core.n2.tone(90)).withOnSurfaceVariant(core.n2.tone(30)).withOutline(core.n2.tone(50)).withOutlineVariant(core.n2.tone(80)).withShadow(core.n1.tone(0)).withScrim(core.n1.tone(0)).withInverseSurface(core.n1.tone(20)).withInverseOnSurface(core.n1.tone(95)).withInversePrimary(core.a1.tone(80)); } private static Scheme darkFromCorePalette(CorePalette core) { - return new Scheme() - .withPrimary(core.a1.tone(80)) - .withOnPrimary(core.a1.tone(20)) - .withPrimaryContainer(core.a1.tone(30)) - .withOnPrimaryContainer(core.a1.tone(90)) - .withSecondary(core.a2.tone(80)) - .withOnSecondary(core.a2.tone(20)) - .withSecondaryContainer(core.a2.tone(30)) - .withOnSecondaryContainer(core.a2.tone(90)) - .withTertiary(core.a3.tone(80)) - .withOnTertiary(core.a3.tone(20)) - .withTertiaryContainer(core.a3.tone(30)) - .withOnTertiaryContainer(core.a3.tone(90)) - .withError(core.error.tone(80)) - .withOnError(core.error.tone(20)) - .withErrorContainer(core.error.tone(30)) - .withOnErrorContainer(core.error.tone(80)) - .withBackground(core.n1.tone(10)) - .withOnBackground(core.n1.tone(90)) - .withSurface(core.n1.tone(10)) - .withOnSurface(core.n1.tone(90)) - .withSurfaceVariant(core.n2.tone(30)) - .withOnSurfaceVariant(core.n2.tone(80)) - .withOutline(core.n2.tone(60)) - .withOutlineVariant(core.n2.tone(30)) - .withShadow(core.n1.tone(0)) - .withScrim(core.n1.tone(0)) - .withInverseSurface(core.n1.tone(90)) - .withInverseOnSurface(core.n1.tone(20)) - .withInversePrimary(core.a1.tone(40)); + return new Scheme().withPrimary(core.a1.tone(80)).withOnPrimary(core.a1.tone(20)).withPrimaryContainer(core.a1.tone(30)).withOnPrimaryContainer(core.a1.tone(90)).withSecondary(core.a2.tone(80)).withOnSecondary(core.a2.tone(20)).withSecondaryContainer(core.a2.tone(30)).withOnSecondaryContainer(core.a2.tone(90)).withTertiary(core.a3.tone(80)).withOnTertiary(core.a3.tone(20)).withTertiaryContainer(core.a3.tone(30)).withOnTertiaryContainer(core.a3.tone(90)).withError(core.error.tone(80)).withOnError(core.error.tone(20)).withErrorContainer(core.error.tone(30)).withOnErrorContainer(core.error.tone(80)).withBackground(core.n1.tone(10)).withOnBackground(core.n1.tone(90)).withSurface(core.n1.tone(10)).withOnSurface(core.n1.tone(90)).withSurfaceVariant(core.n2.tone(30)).withOnSurfaceVariant(core.n2.tone(80)).withOutline(core.n2.tone(60)).withOutlineVariant(core.n2.tone(30)).withShadow(core.n1.tone(0)).withScrim(core.n1.tone(0)).withInverseSurface(core.n1.tone(90)).withInverseOnSurface(core.n1.tone(20)).withInversePrimary(core.a1.tone(40)); } public int getPrimary() { @@ -590,68 +510,10 @@ public Scheme withInversePrimary(int inversePrimary) { return this; } + @NonNull @Override public String toString() { - return "Scheme{" - + "primary=" - + primary - + ", onPrimary=" - + onPrimary - + ", primaryContainer=" - + primaryContainer - + ", onPrimaryContainer=" - + onPrimaryContainer - + ", secondary=" - + secondary - + ", onSecondary=" - + onSecondary - + ", secondaryContainer=" - + secondaryContainer - + ", onSecondaryContainer=" - + onSecondaryContainer - + ", tertiary=" - + tertiary - + ", onTertiary=" - + onTertiary - + ", tertiaryContainer=" - + tertiaryContainer - + ", onTertiaryContainer=" - + onTertiaryContainer - + ", error=" - + error - + ", onError=" - + onError - + ", errorContainer=" - + errorContainer - + ", onErrorContainer=" - + onErrorContainer - + ", background=" - + background - + ", onBackground=" - + onBackground - + ", surface=" - + surface - + ", onSurface=" - + onSurface - + ", surfaceVariant=" - + surfaceVariant - + ", onSurfaceVariant=" - + onSurfaceVariant - + ", outline=" - + outline - + ", outlineVariant=" - + outlineVariant - + ", shadow=" - + shadow - + ", scrim=" - + scrim - + ", inverseSurface=" - + inverseSurface - + ", inverseOnSurface=" - + inverseOnSurface - + ", inversePrimary=" - + inversePrimary - + '}'; + return "Scheme{" + "primary=" + primary + ", onPrimary=" + onPrimary + ", primaryContainer=" + primaryContainer + ", onPrimaryContainer=" + onPrimaryContainer + ", secondary=" + secondary + ", onSecondary=" + onSecondary + ", secondaryContainer=" + secondaryContainer + ", onSecondaryContainer=" + onSecondaryContainer + ", tertiary=" + tertiary + ", onTertiary=" + onTertiary + ", tertiaryContainer=" + tertiaryContainer + ", onTertiaryContainer=" + onTertiaryContainer + ", error=" + error + ", onError=" + onError + ", errorContainer=" + errorContainer + ", onErrorContainer=" + onErrorContainer + ", background=" + background + ", onBackground=" + onBackground + ", surface=" + surface + ", onSurface=" + onSurface + ", surfaceVariant=" + surfaceVariant + ", onSurfaceVariant=" + onSurfaceVariant + ", outline=" + outline + ", outlineVariant=" + outlineVariant + ", shadow=" + shadow + ", scrim=" + scrim + ", inverseSurface=" + inverseSurface + ", inverseOnSurface=" + inverseOnSurface + ", inversePrimary=" + inversePrimary + '}'; } @Override @@ -662,9 +524,6 @@ public boolean equals(Object object) { if (!(object instanceof Scheme scheme)) { return false; } - if (!super.equals(object)) { - return false; - } if (primary != scheme.primary) { return false; @@ -755,7 +614,7 @@ public boolean equals(Object object) { @Override public int hashCode() { - int result = super.hashCode(); + int result = System.identityHashCode(this); result = 31 * result + primary; result = 31 * result + onPrimary; result = 31 * result + primaryContainer; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeContent.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeContent.java index 18f58c9b..8c9b1d1e 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeContent.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeContent.java @@ -17,6 +17,8 @@ package com.drdisagree.colorblendr.utils.monet.scheme; import com.drdisagree.colorblendr.utils.monet.dislike.DislikeAnalyzer; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.temperature.TemperatureCache; @@ -35,22 +37,6 @@ */ public class SchemeContent extends DynamicScheme { public SchemeContent(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.CONTENT, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma()), - TonalPalette.fromHueAndChroma( - sourceColorHct.getHue(), - Math.max(sourceColorHct.getChroma() - 32.0, sourceColorHct.getChroma() * 0.5)), - TonalPalette.fromHct( - DislikeAnalyzer.fixIfDisliked( - new TemperatureCache(sourceColorHct) - .getAnalogousColors(/* count= */ 3, /* divisions= */ 6) - .get(2))), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma() / 8.0), - TonalPalette.fromHueAndChroma( - sourceColorHct.getHue(), (sourceColorHct.getChroma() / 8.0) + 4.0)); + super(sourceColorHct, Variant.CONTENT, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma()), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), Math.max(sourceColorHct.getChroma() - 32.0, sourceColorHct.getChroma() * 0.5)), TonalPalette.fromHct(DislikeAnalyzer.fixIfDisliked(new TemperatureCache(sourceColorHct).getAnalogousColors(/* count= */ 3, /* divisions= */ 6).get(2))), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma() / 8.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), (sourceColorHct.getChroma() / 8.0) + 4.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeExpressive.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeExpressive.java index d33df964..4886bc34 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeExpressive.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeExpressive.java @@ -16,6 +16,8 @@ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; @@ -30,20 +32,6 @@ public class SchemeExpressive extends DynamicScheme { private static final double[] TERTIARY_ROTATIONS = {120, 120, 20, 45, 20, 15, 20, 120, 120}; public SchemeExpressive(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.EXPRESSIVE, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 240.0), 40.0), - TonalPalette.fromHueAndChroma( - DynamicScheme.getRotatedHue(sourceColorHct, HUES, SECONDARY_ROTATIONS), 24.0), - TonalPalette.fromHueAndChroma( - DynamicScheme.getRotatedHue(sourceColorHct, HUES, TERTIARY_ROTATIONS), 32.0), - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 15.0), 8.0), - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 15.0), 12.0)); + super(sourceColorHct, Variant.EXPRESSIVE, isDark, contrastLevel, TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 240.0), 40.0), TonalPalette.fromHueAndChroma(DynamicScheme.getRotatedHue(sourceColorHct, HUES, SECONDARY_ROTATIONS), 24.0), TonalPalette.fromHueAndChroma(DynamicScheme.getRotatedHue(sourceColorHct, HUES, TERTIARY_ROTATIONS), 32.0), TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 15.0), 8.0), TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 15.0), 12.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFidelity.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFidelity.java index 036f3832..d19e0a66 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFidelity.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFidelity.java @@ -17,6 +17,8 @@ package com.drdisagree.colorblendr.utils.monet.scheme; import com.drdisagree.colorblendr.utils.monet.dislike.DislikeAnalyzer; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.temperature.TemperatureCache; @@ -33,19 +35,6 @@ */ public class SchemeFidelity extends DynamicScheme { public SchemeFidelity(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.FIDELITY, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma()), - TonalPalette.fromHueAndChroma( - sourceColorHct.getHue(), - Math.max(sourceColorHct.getChroma() - 32.0, sourceColorHct.getChroma() * 0.5)), - TonalPalette.fromHct( - DislikeAnalyzer.fixIfDisliked(new TemperatureCache(sourceColorHct).getComplement())), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma() / 8.0), - TonalPalette.fromHueAndChroma( - sourceColorHct.getHue(), (sourceColorHct.getChroma() / 8.0) + 4.0)); + super(sourceColorHct, Variant.FIDELITY, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma()), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), Math.max(sourceColorHct.getChroma() - 32.0, sourceColorHct.getChroma() * 0.5)), TonalPalette.fromHct(DislikeAnalyzer.fixIfDisliked(new TemperatureCache(sourceColorHct).getComplement())), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), sourceColorHct.getChroma() / 8.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), (sourceColorHct.getChroma() / 8.0) + 4.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFruitSalad.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFruitSalad.java index 594ce04c..a33f9a0c 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFruitSalad.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeFruitSalad.java @@ -15,6 +15,8 @@ */ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; @@ -24,17 +26,6 @@ */ public class SchemeFruitSalad extends DynamicScheme { public SchemeFruitSalad(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.FRUIT_SALAD, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() - 50.0), 48.0), - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() - 50.0), 36.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 36.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 10.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0)); + super(sourceColorHct, Variant.FRUIT_SALAD, isDark, contrastLevel, TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() - 50.0), 48.0), TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() - 50.0), 36.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 36.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 10.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0)); } } \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeMonochrome.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeMonochrome.java index 92c3788f..22137409 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeMonochrome.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeMonochrome.java @@ -16,6 +16,8 @@ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; @@ -24,15 +26,6 @@ */ public class SchemeMonochrome extends DynamicScheme { public SchemeMonochrome(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.MONOCHROME, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0)); + super(sourceColorHct, Variant.MONOCHROME, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeNeutral.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeNeutral.java index 203cd586..a8afb7fd 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeNeutral.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeNeutral.java @@ -15,6 +15,8 @@ */ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; @@ -23,15 +25,6 @@ */ public class SchemeNeutral extends DynamicScheme { public SchemeNeutral(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.NEUTRAL, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 12.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 2.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 2.0)); + super(sourceColorHct, Variant.NEUTRAL, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 12.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 2.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 2.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeRainbow.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeRainbow.java index 381b6e54..296c2324 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeRainbow.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeRainbow.java @@ -15,6 +15,8 @@ */ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; @@ -24,16 +26,6 @@ */ public class SchemeRainbow extends DynamicScheme { public SchemeRainbow(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.RAINBOW, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 48.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0)); + super(sourceColorHct, Variant.RAINBOW, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 48.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0)); } } \ No newline at end of file diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeTonalSpot.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeTonalSpot.java index 77b87ce7..28f26a3e 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeTonalSpot.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeTonalSpot.java @@ -15,6 +15,8 @@ */ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; import com.drdisagree.colorblendr.utils.monet.utils.MathUtils; @@ -24,16 +26,6 @@ */ public class SchemeTonalSpot extends DynamicScheme { public SchemeTonalSpot(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.TONAL_SPOT, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 36.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), - TonalPalette.fromHueAndChroma( - MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 6.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0)); + super(sourceColorHct, Variant.TONAL_SPOT, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 36.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 16.0), TonalPalette.fromHueAndChroma(MathUtils.sanitizeDegreesDouble(sourceColorHct.getHue() + 60.0), 24.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 6.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 8.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeVibrant.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeVibrant.java index f4c53a14..f6470af1 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeVibrant.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/scheme/SchemeVibrant.java @@ -15,6 +15,8 @@ */ package com.drdisagree.colorblendr.utils.monet.scheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.DynamicScheme; +import com.drdisagree.colorblendr.utils.monet.dynamiccolor.Variant; import com.drdisagree.colorblendr.utils.monet.hct.Hct; import com.drdisagree.colorblendr.utils.monet.palettes.TonalPalette; @@ -27,17 +29,6 @@ public class SchemeVibrant extends DynamicScheme { private static final double[] TERTIARY_ROTATIONS = {35, 30, 20, 25, 30, 35, 30, 25, 25}; public SchemeVibrant(Hct sourceColorHct, boolean isDark, double contrastLevel) { - super( - sourceColorHct, - Variant.VIBRANT, - isDark, - contrastLevel, - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 200.0), - TonalPalette.fromHueAndChroma( - DynamicScheme.getRotatedHue(sourceColorHct, HUES, SECONDARY_ROTATIONS), 24.0), - TonalPalette.fromHueAndChroma( - DynamicScheme.getRotatedHue(sourceColorHct, HUES, TERTIARY_ROTATIONS), 32.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 10.0), - TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 12.0)); + super(sourceColorHct, Variant.VIBRANT, isDark, contrastLevel, TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 200.0), TonalPalette.fromHueAndChroma(DynamicScheme.getRotatedHue(sourceColorHct, HUES, SECONDARY_ROTATIONS), 24.0), TonalPalette.fromHueAndChroma(DynamicScheme.getRotatedHue(sourceColorHct, HUES, TERTIARY_ROTATIONS), 32.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 10.0), TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 12.0)); } } diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/score/Score.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/score/Score.java index 7be176e5..9754079e 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/score/Score.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/score/Score.java @@ -52,8 +52,7 @@ public static List score(Map colorsToPopulation, int return score(colorsToPopulation, desired, 0xff4285f4, true); } - public static List score( - Map colorsToPopulation, int desired, int fallbackColorArgb) { + public static List score(Map colorsToPopulation, int desired, int fallbackColorArgb) { return score(colorsToPopulation, desired, fallbackColorArgb, true); } @@ -71,11 +70,7 @@ public static List score( * the input colors were not suitable for a theme, a default fallback color will be provided, * Google Blue. */ - public static List score( - Map colorsToPopulation, - int desired, - int fallbackColorArgb, - boolean filter) { + public static List score(Map colorsToPopulation, int desired, int fallbackColorArgb, boolean filter) { // Get the HCT color for each Argb value, while finding the per hue count and // total count. @@ -111,8 +106,7 @@ public static List score( } double proportionScore = proportion * 100.0 * WEIGHT_PROPORTION; - double chromaWeight = - hct.getChroma() < TARGET_CHROMA ? WEIGHT_CHROMA_BELOW : WEIGHT_CHROMA_ABOVE; + double chromaWeight = hct.getChroma() < TARGET_CHROMA ? WEIGHT_CHROMA_BELOW : WEIGHT_CHROMA_ABOVE; double chromaScore = (hct.getChroma() - TARGET_CHROMA) * chromaWeight; double score = proportionScore + chromaScore; scoredHcts.add(new ScoredHCT(hct, score)); diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/temperature/TemperatureCache.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/temperature/TemperatureCache.java index be057bff..2363040f 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/temperature/TemperatureCache.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/temperature/TemperatureCache.java @@ -74,10 +74,7 @@ public static double rawTemperature(Hct color) { double[] lab = ColorUtils.labFromArgb(color.toInt()); double hue = MathUtils.sanitizeDegreesDouble(Math.toDegrees(Math.atan2(lab[2], lab[1]))); double chroma = Math.hypot(lab[1], lab[2]); - return -0.5 - + 0.02 - * Math.pow(chroma, 1.07) - * Math.cos(Math.toRadians(MathUtils.sanitizeDegreesDouble(hue - 50.))); + return -0.5 + 0.02 * Math.pow(chroma, 1.07) * Math.cos(Math.toRadians(MathUtils.sanitizeDegreesDouble(hue - 50.))); } /** @@ -118,14 +115,12 @@ public Hct getComplement() { // Find the color in the other section, closest to the inverse percentile // of the input color. This is the complement. for (double hueAddend = 0.; hueAddend <= 360.; hueAddend += 1.) { - double hue = MathUtils.sanitizeDegreesDouble( - startHue + directionOfRotation * hueAddend); + double hue = MathUtils.sanitizeDegreesDouble(startHue + directionOfRotation * hueAddend); if (!isBetween(hue, startHue, endHue)) { continue; } Hct possibleAnswer = getHctsByHue().get((int) Math.round(hue)); - double relativeTemp = - (getTempsByHct().get(possibleAnswer) - coldestTemp) / range; + double relativeTemp = (getTempsByHct().get(possibleAnswer) - coldestTemp) / range; double error = Math.abs(complementRelativeTemp - relativeTemp); if (error < smallestError) { smallestError = error; @@ -252,8 +247,7 @@ public List getAnalogousColors(int count, int divisions) { */ public double getRelativeTemperature(Hct hct) { double range = getTempsByHct().get(getWarmest()) - getTempsByHct().get(getColdest()); - double differenceFromColdest = - getTempsByHct().get(hct) - getTempsByHct().get(getColdest()); + double differenceFromColdest = getTempsByHct().get(hct) - getTempsByHct().get(getColdest()); // Handle when there's no difference in temperature between warmest and // coldest: for example, at T100, only one color is available, white. if (range == 0.) { @@ -305,8 +299,7 @@ private List getHctsByTemp() { List hcts = new ArrayList<>(getHctsByHue()); hcts.add(input); - Comparator temperaturesComparator = - Comparator.comparing((Hct arg) -> getTempsByHct().get(arg), Double::compareTo); + Comparator temperaturesComparator = Comparator.comparing((Hct arg) -> getTempsByHct().get(arg), Double::compareTo); Collections.sort(hcts, temperaturesComparator); precomputedHctsByTemp = hcts; return precomputedHctsByTemp; diff --git a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/utils/ColorUtils.java b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/utils/ColorUtils.java index f35ad957..3c1ab1b1 100644 --- a/app/src/main/java/com/drdisagree/colorblendr/utils/monet/utils/ColorUtils.java +++ b/app/src/main/java/com/drdisagree/colorblendr/utils/monet/utils/ColorUtils.java @@ -25,24 +25,8 @@ * CAM16. */ public class ColorUtils { - static final double[][] SRGB_TO_XYZ = - new double[][]{ - new double[]{0.41233895, 0.35762064, 0.18051042}, - new double[]{0.2126, 0.7152, 0.0722}, - new double[]{0.01932141, 0.11916382, 0.95034478}, - }; - static final double[][] XYZ_TO_SRGB = - new double[][]{ - new double[]{ - 3.2413774792388685, -1.5376652402851851, -0.49885366846268053, - }, - new double[]{ - -0.9691452513005321, 1.8758853451067872, 0.04156585616912061, - }, - new double[]{ - 0.05562093689691305, -0.20395524564742123, 1.0571799111220335, - }, - }; + static final double[][] SRGB_TO_XYZ = new double[][]{new double[]{0.41233895, 0.35762064, 0.18051042}, new double[]{0.2126, 0.7152, 0.0722}, new double[]{0.01932141, 0.11916382, 0.95034478},}; + static final double[][] XYZ_TO_SRGB = new double[][]{new double[]{3.2413774792388685, -1.5376652402851851, -0.49885366846268053,}, new double[]{-0.9691452513005321, 1.8758853451067872, 0.04156585616912061,}, new double[]{0.05562093689691305, -0.20395524564742123, 1.0571799111220335,},}; static final double[] WHITE_POINT_D65 = new double[]{95.047, 100.0, 108.883}; private ColorUtils() { diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index 44a50d04..f7ad1bdd 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -138,13 +138,13 @@ استخدم صلاحيات الروت لتغيير الألوان. جميع خيارات التخصيص ستكون متاحة. Shizuku استخدم Shizuku للعمل بدون روت. ستكون خيارات التخصيص المحدودة متاحة. - Color palette - Explore all the colors of your material palette - Tap on any color in the palette to customize its appearance; long press to reset to the default color. - Tap on any color in the palette to copy its hexadecimal color code. - Select app category - System apps - User apps - Launchable apps - All apps + لوحة الألوان + استكشف جميع ألوان لوحة Material You الخاصة بك + اضغط على أي لون في اللوحة لتخصيص مظهرها؛ اضغط لفترة طويلة لإعادة التعيين لللون الافتراضي. + اضغط على أي لون في اللوحة لنسخ رمز اللون سداسي المستوى الخاص بها. + اختر فئة التطبيق + تطبيقات النظام + تطبيقات المستخدم + تطبيقات قابلة للتشغيل + كل التطبيقات diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 8b080237..731f8ef8 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -36,7 +36,7 @@ Výběr barvy Resetovat Předvolba - Obraz + Z fotky Vybrat barvu z obrázku Výběr obrázku Udělit oprávnění @@ -145,6 +145,6 @@ Vyberte kategorii aplikace Systémové aplikace Uživatelské aplikace - Launchable apps - All apps + Spustitelné aplikace + Všechny aplikace diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index daa4acef..5088e223 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -1,21 +1,21 @@ - Express yourself with colors - Elevate your creativity with effortless material customization. Instantly tweak colors for a personalized touch in just a few taps. + Exprésate con colores + Eleva tu creatividad con personalización material sin esfuerzos. Retoca colores para un toque personalizado en pocos pasos. Neutral - Monochrome - Tonal Spot - Vibrant - Rainbow - Expressive - Fidelity - Fruit Salad - Content - Accurate shades - Keep accurate shades for accent colors - Monet style - Accent saturation - Background saturation + Monocromático + Espacio Tonal + Vibrante + Arcoíris + Expresivo + Fidelidad + Ensalada de Frutas + Contenido + Sombras precisas + Mantener sombras precisas en los acentos de color + Estilo Monet + Saturación del acento + Saturación del fondo Background lightness Ícono Restablecer @@ -37,22 +37,22 @@ Restablecer Preset Image - Pick Color from Image - Image Picker - Grant Permissions - Please grant the \'External Storage\' permissions to continue. - Couldn\'t use this image. - All Photos - Permission denied. - Grant - Custom primary color - Generate palette from custom color + Seleccionar color desde la imagen + Seleccionador de imagen + Garantizar permisos + Por favor, garantiza el permiso \"Almacenamiento Externo\" para continuar. + No se pudo usar esta imagen. + Todas las fotos + Permiso denegado. + Permitir + Color primario personalizado + Generar paleta a partir de un color personalizado Conoce a los desarrolladores ¿Estás disfrutando esta app? Considera apoyarme con un café - Override colors manually - Tap on the color palette preview to override individual colors; long press to reset - Override + Sobreescribir manualmente los colores + Presiona en la previsualización de la paleta de colores para sobreescribir colores individuales; haz una pulsación larga para reiniciar + Sobreescribir Copy Color code: %s Cannot override this color @@ -75,20 +75,20 @@ Root permission not granted. Shizuku permission not granted. Something went wrong. - Permission must be granted + Es necesario garantizar permisos Permitir - Background service + Actividad en segundo plano Notificación sin función. Toca para desactivar. - Background service + Actividad en segundo plano Puedes desactivar estas notificaciones. - Files access required to generate wallpaper colors. + Es necesario el acceso a archivos para generar colores de fondos de pantalla. Onboarding image Post Notifications - This permission is required to keep the service alive in background. - Read Media Images - This permission is required to retrieve home and lockscreen wallpaper. - All Files Access - This permission is required to retrieve home and lockscreen wallpaper. + Este permiso es necesario para mantener el servicio funcionando en segundo plano. + Leer Imágenes del Almacenamiento + Este permiso es necesario para acceder a los fondos de pantalla del inicio y la pantalla de bloqueo. + Acceso a Todos los Archivos + Este permiso es necesario para acceder a los fondos de pantalla del inicio y la pantalla de bloqueo. Iniciar Continuar Lively and refreshing color palette inspired by the effervescence of spritz beverages. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 096d79e4..197fccb4 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -71,17 +71,17 @@ Újraindítás Ismét Biztos vagy benne? - This will override your current theme and configuration. - Root permission not granted. - Shizuku permission not granted. - Something went wrong. - Permission must be granted + Ez felülírja a jelenlegi téma beállításaidat. + Nincs root engedély megadva. + Nincs Shizuku engedély megadva. + Valami félre ment. + Az engedélyt meg kell adnod Engedélyezés Háttérszolgáltatás - Useless notification. Tap to disable. + Haszontalan értesítés. Koppints a letiltáshoz. Háttérszolgáltatás - You can turn off these notifications. - Files access required to generate wallpaper colors. + Ezeket az értesítéseket kikapcsolhatod. + A háttérkép színeinek létrehozásához szükséges fájlok elérése. Onboarding image Post Notifications This permission is required to keep the service alive in background. diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 69e7e8c9..bbbc15f0 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -1,108 +1,108 @@ Express yourself with colors - Elevate your creativity with effortless material customization. Instantly tweak colors for a personalized touch in just a few taps. - Neutral - Monochrome - Tonal Spot - Vibrant - Rainbow - Expressive - Fidelity - Fruit Salad - Content - Accurate shades - Keep accurate shades for accent colors - Monet style - Accent saturation - Background saturation - Background lightness - Icon - Reset - Selected: %s - Selected: %s%s - Apply customization - Learner | Developer | Designer - News - Support - Github - Version - Pitch black theme - Black background in dark mode - If toggles don\'t apply instantly, try adjusting one of the sliders to apply changes promptly. - Color picker preview - Custom primary color - Create palette based on custom color - Color Picker + マテリアルを簡単にカスタマイズして、創造性を高めましょう。 数回タップをするだけで、パーソナライズされたカラーへ瞬時に微調整ができます。 + ニュートラル + モノクロ + トーン スポット + バイブラント + レインボー + エクスプレッシブ + フィデリティ + フルーツサラダ + コンテンツ + 正確な色合い + アクセントカラーの正確な色合いを維持します + Monet スタイル + アクセントの彩度 + 背景の彩度 + 背景の明るさ + アイコン + リセット + 選択済み: %s + 選択済み: %s%s + カスタマイズを適用 + 学習 | 開発者 | デザイナー + ニュース + サポート + GitHub + バージョン + ピッチブラックテーマ + ダークモードで黒い背景を使用します + 切り替えがすぐに適用されない場合は、スライダーを調整して変更を適用してみてください。 + カラーピッカープレビュー + カスタムプライマリカラー + パレットに基づいたカスタムカラーを生成 + カラーピッカー リセット - Preset - Image + プリセット + 画像 画像から色を選択 - Image Picker - Grant Permissions - Please grant the \'External Storage\' permissions to continue. - Couldn\'t use this image. - All Photos + 画像ピッカー + 権限の許可 + 続行するには、「外部ストレージ」の権限を許可してください。 + この画像は使用できませんでした。 + すべての画像 権限が拒否されました。 - Grant - Custom primary color - Generate palette from custom color - Meet the developer - Enjoying this app? - Consider buying me a coffee - Override colors manually - Tap on the color palette preview to override individual colors; long press to reset - Override - Copy - Color code: %s - Cannot override this color - Dismiss - Custom color reset successfully - Reset all - All colors reset successfully + 許可 + カスタムプライマリカラー + カスタムカラーからパレットを生成 + 開発者に会う + アプリに満足していただいていますか? + コーヒーを奢ってくれると嬉しいです + 色の編集 + カラーパレットのプレビューをタップすると、個々の色を上書きできます。長押しするとリセットされます。 + 上書き + コピー + カラーコード: %s + この色を上書きすることはできません + 破棄 + カスタムカラーのリセットが成功しました + すべてリセット + すべてのカラーのリセットが成功しました 設定のバックアップ & 復元 - Share the delight of your theme\'s unique look and feel by saving and sharing your configuration + 設定を保存して共有することで、テーマのユニークなルック & フィールの魅力を分かち合いましょう。 バックアップ 復元 - Backup successful - Backup failed - Restore successful - Restore failed - Restart - Retry - Are you sure? - This will override your current theme and configuration. - Root permission not granted. - Shizuku permission not granted. - Something went wrong. - Permission must be granted - Grant - Background service - Useless notification. Tap to disable. - Background service - You can turn off these notifications. - Files access required to generate wallpaper colors. - Onboarding image - Post Notifications - This permission is required to keep the service alive in background. - Read Media Images - This permission is required to retrieve home and lockscreen wallpaper. - All Files Access - This permission is required to retrieve home and lockscreen wallpaper. - Start - Continue - Lively and refreshing color palette inspired by the effervescence of spritz beverages. - Harmonious scheme using variations of a single hue for a sleek and minimalist aesthetic. - Palette featuring subtle variations in tone, creating a sophisticated and nuanced visual impact. - Energetic and dynamic color combination that exudes liveliness and intensity. - Diverse spectrum of colors, symbolizing diversity, playfulness, and a wide range of possibilities. - Bold and expressive mix of colors that conveys emotion and creativity. - Faithful representation of colors, ensuring accuracy and authenticity in visual representation. - Eclectic blend of colors reminiscent of a vibrant assortment of fresh and juicy fruits. - Balanced and calming palette, promoting a sense of tranquility and contentment. - You may have noticed that certain apps not following system theme, a result of the MaterialComponents update (v1.11.0+). Manually select those apps here to apply the theme, and remember to close and reopen those apps to see the changes take effect. - Colors - Per app theme + バックアップが成功しました + バックアップに失敗しました + 復元が成功しました + 復元に失敗しました + 再起動 + 再試行 + よろしいですか? + これによって現在のテーマと設定が上書きされます。 + root 権限が許可されていません。 + Shizuku の権限が許可されていません。 + 問題が発生しました。 + 権限を許可する必要があります + 許可 + バックグラウンドサービス + 不要な通知です。タップして無効化します。 + バックグラウンドサービス + これらの通知を OFF にすることができます。 + 壁紙の色を生成するにはファイルへのアクセス許可が必要です。 + オンボーディングの画像 + 通知の投稿 + サービスをバックグラウンドで稼働させるためにこの権限が必要です。 + メディア画像の読み取り + ホーム画面とロック画面の壁紙を取得するためにこの権限が必要です。 + すべてのファイルへのアクセス + ホーム画面とロック画面の壁紙を取得するためにこの権限が必要です。 + 開始 + 続行 + スプリッツの飲み物にインスパイアされた、生き生きとした爽やかなカラーパレットです。 + 単一の色調のバリエーションを使用した調和のとれたスキームで、洗練されたミニマリストの美学を実現します。 + 微妙な色調の変化が特徴のパレットで、洗練されたニュアンスのある視覚的なインパクトを生み出します。 + 活気と強さを醸し出す、エネルギッシュなダイナミックカラーの組み合わせです。 + 多様性、遊び心、幅広い可能性を象徴する多様なカラースペクトラムです。 + 感情と創造性を伝える大胆で表現力豊かなカラーの組み合わせです。 + 色彩を忠実に表現し、視覚的表現の正確性と信頼性を確保します。 + 新鮮でジューシーなフルーツの鮮やかな組み合わせを思わせる、多彩でブレンドされた色彩です。 + バランスのとれた落ち着いたパレットで、平穏と満足感をもたらします。 + MaterialComponents アップデート (v1.11.0+) の結果、特定のアプリでシステムテーマに従っていないことにお気づきかもしれません。それらのアプリを手動で選択してテーマを適用して、変更が有効化されたことを確認するにはアプリを閉じて再度開くことを忘れないでください。 + カラー + アプリごとのテーマ 設定 スタイル 外観 @@ -110,41 +110,41 @@ プリセット アプリにテーマを強制 Monetに従わないアプリにテーマを適用する - Back + 戻る 権限 - Choose method + 選択の方式 自動起動 システムの起動時に自動で起動する - %s will automatically initiate its service upon system boot.
]]>If this is not the case, please verify whether your system or any third-party tools may be restricting %s. For detailed instructions, you can refer to Don\'t kill my app!]]>.
+ %s はシステムの起動時に自動的にサービスを開始します。
]]>システムやサードパーティーなアプリが %s を制限していないか確認をしてください。詳細な手順については、Don\'t kill my app!]]> を参照してください。
ColorBlendrについて このアプリとその開発者の情報 - Search here… - Note: - Please grant all permissions to proceed - %s service - Theming is active - Theming is inactive - Tint text color - Maintain a subtle tint for text - Primary - Secondary - Tertiary - Neutral 1 - Neutral 2 + 検索… + 注意: + 続行するにはすべての権限を許可してください + %s 個のサービス + テーマはアクティブです + テーマは非アクティブです + テキストの色を着色する + テキストの微妙な色合いを保持します + プライマリ + セカンダリ + トライナリ + ニュートラル 1 + ニュートラル 2 長押しでリセット - Please be aware that the outcome of this action is definitive and cannot be reverted. - Select a method to proceed + このアクションは、最終的に元に戻すことができなくなることにご注文ください。 + 処理の方法を選択 Root - Use root access to change colors. All customization options will be available. + カラーの変更に root 権限を使用します。すべてのカスタマイズオプションが使用可能です。 Shizuku - Use shizuku to work without root. Limited customization options will be available. - Color palette - Explore all the colors of your material palette - Tap on any color in the palette to customize its appearance; long press to reset to the default color. - Tap on any color in the palette to copy its hexadecimal color code. - Select app category - System apps - User apps - Launchable apps - All apps + カラーの変更に Shizuku を使用します。これは 非 root 環境でも使用可能です。一部のカスタマイズオプションが使用可能です。 + カラーパレット + マテリアルパレットのすべてのカラーを探します + パレット内の任意の色をタップして外観をカスタマイズします。長押しするとデフォルトの色にリセットされます。 + パレット内の任意の色をタップすると、16 進数のカラーコードをコピーできます。 + アプリのカテゴリーを選択 + システムアプリ + ユーザーアプリ + 起動可能なアプリ + すべてのアプリ
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 13bd6eea..242d8cec 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -145,6 +145,6 @@ Selecionar categoria do app Apps do sistema Apps do usuário - Launchable apps - All apps + Apps iniciáveis + Todos os apps
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 404b002b..3d88711f 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -138,13 +138,13 @@ Использовать root доступ для смены цветов. Все возможности кастомизации будут доступны. Shizuku Использовать shizuku для работы без root. Некоторые возможности кастомизации не будут доступны. - Color palette - Explore all the colors of your material palette - Tap on any color in the palette to customize its appearance; long press to reset to the default color. - Tap on any color in the palette to copy its hexadecimal color code. - Select app category - System apps - User apps - Launchable apps - All apps + Цветовая палитра + Посмотреть все цвета вашей материальной палитры + Нажмите на любой цвет в палитре, чтобы настроить его внешний вид; долгое нажатие для сброса цвета по умолчанию. + Нажмите на любой цвет в палитре чтобы скопировать его шестнадцатеричный код цвета. + Выбрать категорию приложения + Системные приложения + Пользовательские приложения + Запускаемые приложения + Все приложения diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 425f8752..5bfe4e29 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -3,39 +3,39 @@ Kendinizi renkler ile ifade edin Yaratıcılığınızı çabasız malzeme özelleştirmesi ile yükseltin. Sadece birkaç dokunuşla renkleri anında ayarlayarak kişisel bir dokunuş ekleyin. Doğal - Tek renkli - Ton noktası + Tek Renkli + Ton Noktası Canlı Gökkuşağı Etkileyici Sadakatli Meyve Salatası İçerik - Keskin tonlar + Keskin Tonlar Vurgu renkleri için keskin tonları sakla Monet stili - Vurgu doygunluğu - Arkaplan doygunluğu - Arkaplan parlaklığı + Vurgu Doygunluğu + Arka Plan Doygunluğu + Arka Plan Parlaklığı Simge Sıfırla - Seçildi %s - Seçildi: %s%s + Seçilen: %s + Seçilen: %s%s Özelleştirmeyi uygula Öğrenici | Geliştirici | Tasarımcı Haberler Destek Github Sürüm - Zifiri karanlık tema - Karanlık modda siyah arkaplan + Saf Siyah Tema + Karanlık modda siyah arka plan Geçişler anında uygulanmazsa, değişiklikleri hemen uygulamak için kaydırıcılardan birini ayarlamayı deneyin. - Renk seçicisini ön izle - Özel birincil renk + Renk Seçici Önizlemesi + Özel Birincil Renk Özel renge göre palet oluştur Renk Seçici Sıfırla - Hazır ayar + Ön Tanımlı Resim Resimden Renk Seç Resim Seçici @@ -44,13 +44,13 @@ Bu resmi kullanamazsınız. Tüm Fotoğraflar İzin reddedildi. - İzin ver - Özel birincil renk + İzin Ver + Özel Birincil Renk Özel renkten palet oluştur Geliştiriciyle tanış Bu uygulamayı beğendiniz mi? Bana bir kahve ısmarlamayı düşünün - Renkleri manuel olarak geçersiz kıl + Renkleri Manuel Olarak Geçersiz Kıl Renkleri tek tek geçersiz kılmak için renk paleti önizlemesine dokunun ardından sıfırlamak için uzun basın Geçersiz kıl Kopyala @@ -60,7 +60,7 @@ Özel renk başarıyla sıfırlandı Tümünü sıfırla Tüm renkler başarıyla sıfırlandı - Yedekle & Geri yükle + Yedekle & Geri Yükle Mutluluğu yaymak için yapılandırmanızı kaydedip paylaşarak temanızın kendine özgü estetiğini yakalayın ve paylaşın Yedekle Geri Yükle @@ -76,15 +76,15 @@ Shizuku izni verilmedi. Bir şeyler ters gitti. İzin verilmesi gerekir - İzin ver - Arkaplan hizmeti + İzin Ver + Arka Plan Hizmeti Yararsız bildirim. Devre dışı bırakmak için dokunun. - Arkaplan hizmeti + Arka Plan Hizmeti Bu bildirimleri kapatabilirsiniz. Duvar kağıdı renklerini oluşturmak için dosyalara erişim gereklidir. İlk katılım resmi Bildirim Gönder - Bu izin, hizmetin arkaplanda canlı kalması için gereklidir. + Bu izin, hizmetin arka planda canlı kalması için gereklidir. Medya Görsellerini Oku Bu izin, ev ve kilit ekranı duvar kağıdını almak için gereklidir. Tüm Dosyalara Eriş @@ -100,15 +100,15 @@ Renklerin aslına uygun temsili, görsel temsilde doğruluk ve özgünlük sağlar. Taze ve sulu meyvelerin canlı çeşitliliğini anımsatan renklerin eklektik karışımı. Dengeli ve sakinleştirici palet, huzur ve memnuniyet duygusunu teşvik eder. - MaterialComponents güncellemesinin (v1.11.0+) bir sonucu olarak bazı uygulamaların sistem temasını takip etmediğini fark etmiş olabilirsiniz. Temayı uygulamak için bu uygulamaları buradan manuel olarak seçin ve değişikliklerin etkili olduğunu görmek için bu uygulamaları kapatıp yeniden açmayı unutmayın. + Belirli uygulamaların sistem temasını takip etmediğini fark etmiş olabilirsiniz, bu da MaterialComponents güncellemesinin bir sonucudur (v1.11.0+). Temayı uygulamak için burada manuel olarak bu uygulamaları seçin ve değişikliklerin etkili olması için bu uygulamaları kapatıp tekrar açmayı unutmayın. Renkler - Uygulama başına tema + Uygulama Başına Tema Ayarlar Stiller Temalar - Duvar kağıdı renkleri - Temel renkler - Uygulama başına temayı zorla + Duvar Kağıdı Renkleri + Temel Renkler + Uygulama Başına Temayı Zorla Sistem monetini takip etmeyen belirli uygulamalara özel renkler uygulayın Geri İzinler @@ -116,15 +116,15 @@ Otomatik başlat Sistem açılırken hizmeti başlat Sistem açıldığında %s hizmeti otomatik olarak başlatılacaktır.
]]>Eğer durum böyle değilse, lütfen sisteminizin veya herhangi bir üçüncü taraf aracın %s\'yi kısıtlayıp kısıtlamadığını doğrulayın. Ayrıntılı talimatlar için Uygulamamı öldürme!]]> bölümünden detaylı bilgilere ulaşabilirsiniz.
- Uygulama hakkında + Uygulama Hakkında Bu uygulama ve geliştiricisi hakkında bilgiler edinin - Burada ara… + Burada arayın… Not: Lütfen devam etmek için tüm izinleri verin - %s hizmeti + %s Hizmeti Temalandırma etkin Temalandırma etkin değil - Metin rengini renklendir + Metin Rengini Renklendir Metin için ince bir renk tonu sağla Birincil İkincil @@ -138,13 +138,13 @@ Renkleri değiştirmek için root erişimini kullanın. Tüm özelleştirme seçenekleri mevcut olacaktır. Shizuku Root olmadan çalışmasını sağlamak için shizuku kullanın. Sınırlı özelleştirme seçenekleri mevcut olacaktır. - Renk paleti + Renk Paleti Malzeme paletinizin tüm renklerini keşfedin Görünümünü özelleştirmek için paletteki herhangi bir renge dokunun; varsayılan renge sıfırlamak için uzun basın. Onaltılık renk kodunu kopyalamak için paletteki herhangi bir rengin üzerine dokunun. Uygulama kategorisi seç - Sistem uygulamaları - Kullanıcı uygulamaları - Launchable apps - All apps + Sistem Uygulamaları + Kullanıcı Uygulamaları + Başlatılabilir Uygulamalar + Tüm Uygulamalar diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 2ba11307..294546fe 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -1,57 +1,57 @@ - Express yourself with colors - Elevate your creativity with effortless material customization. Instantly tweak colors for a personalized touch in just a few taps. - Neutral - Monochrome - Tonal Spot - Vibrant - Rainbow - Expressive - Fidelity - Fruit Salad - Content - Accurate shades - Keep accurate shades for accent colors - Monet style - Accent saturation - Background saturation - Background lightness - Icon - Reset - Selected: %s - Selected: %s%s - Apply customization - Learner | Developer | Designer - News - Support + Виражайте себе за допомогою кольорів + Розкрийте творчий потенціал із легким налаштуванням елементів. Миттєво змінюйте кольори для персоналізації лише кількома дотиками. + Нейтральний + Монохромний + Тональна пляма + Яскравий + Різнобарвний + Насичений + Реалістичний + Фруктовий салат + Вміст + Точні відтінки + Зберегти точні відтінки для акцентних кольорів + Стиль Моне + Насиченість акценту + Насиченість фону + Підсвічування фону + Піктограма + Скинути + Вибрано: %s + Вибрано: %s%s + Застосувати налаштування + Студент | Розробник | Дизайнер + Новини + Підтримка Github - Version - Pitch black theme - Black background in dark mode - If toggles don\'t apply instantly, try adjusting one of the sliders to apply changes promptly. - Color picker preview - Custom primary color - Create palette based on custom color - Color Picker - Reset - Preset - Image - Pick Color from Image - Image Picker - Grant Permissions - Please grant the \'External Storage\' permissions to continue. - Couldn\'t use this image. - All Photos - Permission denied. - Grant - Custom primary color - Generate palette from custom color - Meet the developer - Enjoying this app? - Consider buying me a coffee - Override colors manually - Tap on the color palette preview to override individual colors; long press to reset + Версія + Темна тема + Чорний фон у темному режимі + Якщо перемикачі не спрацьовують миттєво, спробуйте налаштувати один із повзунків, щоб зміни відразу застосовувалися. + Попередній перегляд палітри кольорів + Налаштувати основний колір + Створити палітру на основі особливого кольору + Піпетка + Скинути + Пресет + Зображення + Взяти колір із зображення + Засіб вибору зображень + Надати дозволи + Надайте дозвіл Зовнішньому сховищу, щоб продовжити. + Не вдалося використати це зображення. + Усі фото + У дозволі відмовлено. + Надати дозвіл + Налаштувати основний колір + Створити палітру з особливого кольору + Познайомитися з розробником + Подобається додаток? + Розгляньте можливість подякувати на Buy Me a Coffe + Перевизначити кольори вручну + Натисніть на попередній перегляд палітри кольорів, щоб замінити окремі кольори; тривале натискання - для скасування Override Copy Color code: %s diff --git a/fastlane/metadata/android/en-US/changelogs/13.txt b/fastlane/metadata/android/en-US/changelogs/13.txt new file mode 100644 index 00000000..6705cfda --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/13.txt @@ -0,0 +1,11 @@ +Changelog: + +• Enhanced color extraction algorithm. +• Improved theme configuration restoration. +• Better live wallpaper color extraction. +• Prevented changes to black and white shades to resolve specific issues. +• Optimized background running service. +• Bug fixes and various improvements. +• Updated translations. + +Translation credit goes to all the contributors on our Crowdin platform. diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index ce8a0474..3a1bb894 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -1,4 +1,4 @@ -ColorBlendr is an application to modify material you colors of your device. +ColorBlendr is an application to modify Material You colors of your device. Elevate your creativity with effortless material customization. Instantly tweak colors for a personalized touch in just a few taps. Features: diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt new file mode 100644 index 00000000..754a00ff --- /dev/null +++ b/fastlane/metadata/android/ru/full_description.txt @@ -0,0 +1,32 @@ +ColorBlendr это приложение для изменения цветов Material You дизайна вашего устройства. +Расширьте свой творческий потенциал с помощью простой настройки материала. Мгновенно настраивайте цвета для персонализации всего за несколько нажатий. + +Особенности: + +- Изменение акцентного насыщения +- Изменение фонового насыщенная +- Изменение фоновой яркости +- Кромешно черная тема в тёмном режиме +- Ручное переопределение цвета +- И многое другое... + +Требования: + +- Устройство Android 12+ +- Доступ root или Shizuku environment + +Инструкции: + +- Установите и откройте приложение +- Дайте разрешения для приложения +- Выберите root или режим Shizuku +- Вот и все. Теперь вы можете настраивать! + +Перевод: + +Помогите в переводе ColorBlendr на ваш предпочитаемый язык через нашей платформе Crowdin. Ваш вклад поможет сделать ColorBlendr доступным для более широкой аудитории. + +Для чего разрешения: + +- QUERY_ALL_PACKAGES : Требуется для индивидуальной темизации сторонних приложений которые имеют поддержку монетной темы но все еще не придерживаются системным цветам. +- MANAGE_EXTERNAL_STORAGE: Требуется для захвата текущих обоев и извлечения доминирующих цветов из обоев. diff --git a/fastlane/metadata/android/ru/short_description.txt b/fastlane/metadata/android/ru/short_description.txt new file mode 100644 index 00000000..8cb53260 --- /dev/null +++ b/fastlane/metadata/android/ru/short_description.txt @@ -0,0 +1 @@ +Выразите себя в цветах \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3e206722..2e113229 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true android.nonTransitiveRClass=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc590945..5f8cd1a2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,21 @@ [versions] -agp = "8.3.2" +agp = "8.4.1" androidUtils = "0.0.5" -annotation = "1.7.1" -appcompat = "1.6.1" +annotation = "1.8.0" +appcompat = "1.7.0" blurView = "version-2.0.3" circleimageview = "3.1.0" constraintlayout = "2.1.4" -coreKtx = "1.12.0" +coreKtx = "1.13.1" coreSplashscreen = "1.0.1" flexbox = "3.0.0" glide = "4.16.0" -gson = "2.10.1" +gson = "2.11.0" hiddenapibypassVersion = "4.3" kapt = "2.0.0-RC1" kotlin = "1.9.23" libsuVersion = "5.2.2" -material = "1.11.0" +material = "1.12.0" palette = "1.0.0" preferenceKtx = "1.2.1" recyclerview = "1.3.2" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8c4445a7..b2b1563c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Dec 04 22:49:30 BDT 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists