├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── amastigote │ │ └── darker │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ ├── amastigote │ │ │ └── darker │ │ │ │ ├── activity │ │ │ │ ├── AboutActivity.java │ │ │ │ └── MainActivity.java │ │ │ │ ├── model │ │ │ │ ├── DarkerNotification.java │ │ │ │ └── DarkerSettings.java │ │ │ │ └── service │ │ │ │ └── ScreenFilterService.java │ │ │ └── rtugeek │ │ │ └── android │ │ │ └── colorseekbar │ │ │ └── ColorSeekBar.java │ └── res │ │ ├── drawable │ │ ├── selector_turn_on_button.xml │ │ ├── shape_checked_toggle_button.xml │ │ ├── shape_text_tag.xml │ │ └── shape_unchecked_toggle_button.xml │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_license.xml │ │ ├── activity_main.xml │ │ ├── content_main.xml │ │ ├── control_panel.xml │ │ └── screen_filter.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_settings_white_24dp.png │ │ └── night_128.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── amastigote │ └── darker │ └── ExampleUnitTest.java ├── build.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | gradlew 9 | gradlew.bat 10 | gradle.properties 11 | gradle/wrapper 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | screen-filter: dims your screen further to protect eyes in complete darkness 2 | Copyright (C) 2016 hwding 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # screen filter 2 | **dims your screen further to protect eyes in complete darkness** 3 | 4 | [![License](https://img.shields.io/badge/LICENSE-GPL%203-blue.svg?style=flat-square)](https://github.com/hwding/make-it-darker/blob/master/LICENSE) 5 | [![Platform](https://img.shields.io/badge/PLATFORM-Android-red.svg?style=flat-square)](https://www.android.com/) 6 | [![Download](https://img.shields.io/badge/DOWNLOAD-coolapk-green.svg?style=flat-square)](http://www.coolapk.com/apk/com.amastigote.darker) 7 | [![Download](https://img.shields.io/badge/DOWNLOAD-wandoujia-green.svg?style=flat-square)](http://www.wandoujia.com/apps/com.amastigote.darker) 8 | 9 | ## UPDATE LOG 10 | 11 | ##### 2019-01-23 12 | - fix the problem that filter could not cover the whole screen after orientation 13 | 14 | ##### 2019-01-17 15 | - fix the problem that filter could not cover navigation bar 16 | - update libraries 17 | 18 | ##### 2017-02-06 19 | - remove snack-bars 20 | - optimize activity switching 21 | - change logo 22 | - update libraries 23 | - several UI improvements (string, layout, etc.) 24 | 25 | ##### 2016-10-16 26 | - add auto brightness option 27 | - add brightness & alpha indicator 28 | - optimize third party ColorSeekBar 29 | - several UI improvements (layout, style, animation, etc.) 30 | 31 | ##### 2016-10-10 32 | - fix the problem that filter could not cover the whole screen after orientation 33 | - update google libraries 34 | - several code adjustments 35 | 36 | ##### 2016-08-23 37 | - update libraries 38 | - fix the problem of over scrolling 39 | - change a third party library for circular seekBar 40 | 41 | ##### 2016-08-13 42 | - set fullscreen flag for screen filter 43 | 44 | ##### 2016-08-09 45 | - remove splash page to simplify apk 46 | - remove useless codes 47 | - fix the bug that screen will dim and lock down even when you are watch videos after activating screen filter, so the 'keep screen on' function is removed 48 | - improve UI 49 | 50 | ##### 2016-08-07 51 | - improve service persistence 52 | - change icon 53 | 54 | ##### 2016-08-06 55 | - remove duplicated methods 56 | - remove unused codes 57 | 58 | ##### 2016-08-04 59 | - fix a bug that screen filter can not cover status bar and lock screen 60 | - fix a bug that your wallpaper will go black when screen filter is activated 61 | - improve code structure 62 | - more power-saving when service is running in background 63 | - more bug fixes 64 | - adjust some indicative information 65 | - adjust UI lightly 66 | 67 | ##### 2016-08-03 68 | - new feature: now you can (/de)activate screen filter through tapping the notification, you can also jump directly onto control panel with our new notification 69 | 70 | ##### 2016-08-01 71 | - redesign UI and interaction 72 | - add splash page 73 | - add quit notification 74 | 75 | ##### 2016-07-30 76 | - add feedback function 77 | - redesign UI and interaction 78 | 79 | ##### 2016-07-28 80 | - update a library to fix the bug that seekBar's pointer won't change its position along with its value 81 | - fix a bug that screen filter will be activated unexpectedly when 'restore default settings' button is pressed 82 | 83 | ##### 2016-07-27 84 | - new feature: now you can adjust the screen filter dynamically as long as it is activated 85 | - fix a bug in 'keep screen on' function 86 | - fix a bug that 'restore default settings' function is not working when screen filter is activated 87 | - change background 88 | 89 | ##### 2016-07-26 90 | - new feature: now you can keep screen on when screen filter is activated 91 | - ui improve, more friendly 92 | - several bug fixes 93 | 94 | ##### 2016-07-25 95 | - improve color picker function 96 | - fix a bug that preference of filter color can not be restored 97 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | //noinspection GroovyMissingReturnStatement 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion '28.0.3' 7 | //aaptOptions.cruncherEnabled = false 8 | //aaptOptions.useNewCruncher = false 9 | 10 | defaultConfig { 11 | applicationId "com.amastigote.darker" 12 | minSdkVersion 19 13 | targetSdkVersion 25 14 | versionCode 16 15 | versionName "0.2.2" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | lintOptions { 25 | checkReleaseBuilds false 26 | // Or, if you prefer, you can continue to check for errors in release builds, 27 | // but continue the build even when errors are found: 28 | abortOnError false 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(include: ['*.jar'], dir: 'libs') 34 | testImplementation 'junit:junit:4.12' 35 | implementation 'com.android.support:appcompat-v7:25.4.0' 36 | implementation 'com.android.support:design:25.4.0' 37 | implementation 'com.android.support:support-v4:25.4.0' 38 | implementation 'me.tankery.lib:circularSeekBar:1.1.7' 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\lenovo\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/amastigote/darker/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/amastigote/darker/activity/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker.activity; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.support.v7.app.AlertDialog; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | 16 | import com.amastigote.darker.R; 17 | 18 | public class AboutActivity extends AppCompatActivity { 19 | TextView versionCodeTextView; 20 | Button licenseButton; 21 | Button feedbackButton; 22 | 23 | public static String getVersionCode(Context context) { 24 | try { 25 | PackageManager packageManager = context.getPackageManager(); 26 | PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_GIDS); 27 | return packageInfo.versionName; 28 | } catch (Exception ignored) { 29 | } 30 | return null; 31 | } 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_about); 37 | 38 | if (null != getSupportActionBar()) { 39 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 40 | } 41 | 42 | versionCodeTextView = (TextView) findViewById(R.id.aa_versionCode); 43 | licenseButton = (Button) findViewById(R.id.aa_licenseButton); 44 | feedbackButton = (Button) findViewById(R.id.aa_feedBackButton); 45 | 46 | versionCodeTextView.setText(getVersionCode(AboutActivity.this)); 47 | 48 | licenseButton.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View view) { 51 | new AlertDialog.Builder(AboutActivity.this) 52 | .setTitle(R.string.license) 53 | .setView(R.layout.activity_license) 54 | .setPositiveButton(R.string.positive, null) 55 | .create() 56 | .show(); 57 | } 58 | }); 59 | 60 | feedbackButton.setOnClickListener(new View.OnClickListener() { 61 | @Override 62 | public void onClick(View view) { 63 | Intent intent = new Intent(Intent.ACTION_SENDTO); 64 | //noinspection SpellCheckingInspection 65 | intent.setData(Uri.parse("mailto:m@amastigote.com")); 66 | intent.putExtra(Intent.EXTRA_SUBJECT, "Feedback from ScreenFilter " + getVersionCode(AboutActivity.this)); 67 | startActivity(intent); 68 | } 69 | }); 70 | } 71 | 72 | @Override 73 | public boolean onOptionsItemSelected(MenuItem item) { 74 | if (item.getItemId() == android.R.id.home) 75 | finish(); 76 | return super.onOptionsItemSelected(item); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/amastigote/darker/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker.activity; 2 | 3 | import android.animation.ArgbEvaluator; 4 | import android.animation.ValueAnimator; 5 | import android.annotation.SuppressLint; 6 | import android.content.BroadcastReceiver; 7 | import android.content.Context; 8 | import android.content.DialogInterface; 9 | import android.content.Intent; 10 | import android.content.IntentFilter; 11 | import android.content.res.ColorStateList; 12 | import android.net.Uri; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.provider.Settings; 16 | import android.support.v4.content.ContextCompat; 17 | import android.support.v7.app.AlertDialog; 18 | import android.support.v7.app.AppCompatActivity; 19 | import android.support.v7.widget.AppCompatButton; 20 | import android.support.v7.widget.Toolbar; 21 | import android.view.KeyEvent; 22 | import android.view.Menu; 23 | import android.view.MenuItem; 24 | import android.view.View; 25 | import android.view.animation.AlphaAnimation; 26 | import android.widget.CompoundButton; 27 | import android.widget.Switch; 28 | import android.widget.TextView; 29 | 30 | import com.amastigote.darker.R; 31 | import com.amastigote.darker.model.DarkerNotification; 32 | import com.amastigote.darker.model.DarkerSettings; 33 | import com.amastigote.darker.service.ScreenFilterService; 34 | import com.rtugeek.android.colorseekbar.ColorSeekBar; 35 | 36 | import me.tankery.lib.circularseekbar.CircularSeekBar; 37 | 38 | public class MainActivity extends AppCompatActivity { 39 | DarkerSettings currentDarkerSettings = new DarkerSettings(); 40 | DarkerNotification darkerNotification; 41 | TextView brightness_indicator; 42 | TextView alpha_indicator; 43 | CircularSeekBar circleSeekBar_brightness; 44 | CircularSeekBar circleSeekBar_alpha; 45 | ColorSeekBar colorSeekBar; 46 | Switch aSwitch; 47 | Switch bSwitch; 48 | AppCompatButton appCompatButton; 49 | Intent intent; 50 | View view; 51 | BroadcastReceiver broadcastReceiver; 52 | boolean isServiceRunning = false; 53 | 54 | @Override 55 | protected void onDestroy() { 56 | doCleanBeforeExit(); 57 | super.onDestroy(); 58 | } 59 | 60 | @Override 61 | protected void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | setContentView(R.layout.activity_main); 64 | 65 | //getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 66 | 67 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 68 | toolbar.setTitle(getResources().getString(R.string.app_name)); 69 | toolbar.setTitleMarginStart(100); 70 | toolbar.setLogo(R.mipmap.night_128); 71 | setSupportActionBar(toolbar); 72 | 73 | DarkerSettings.initializeContext(getApplicationContext()); 74 | 75 | checkPermissions(); 76 | 77 | darkerNotification = new DarkerNotification(MainActivity.this); 78 | darkerNotification.updateStatus(isServiceRunning); 79 | 80 | view = findViewById(R.id.cm); 81 | 82 | circleSeekBar_brightness = (CircularSeekBar) findViewById(R.id.cp_brightness_circleSeekBar); 83 | circleSeekBar_alpha = (CircularSeekBar) findViewById(R.id.cp_alpha_circleSeekBar); 84 | 85 | brightness_indicator = (TextView) findViewById(R.id.cp_brightness_indicator); 86 | alpha_indicator = (TextView) findViewById(R.id.cp_alpha_indicator); 87 | 88 | colorSeekBar = (ColorSeekBar) findViewById(R.id.cp_colorSeekBar); 89 | 90 | aSwitch = (Switch) findViewById(R.id.cp_useColor_switch); 91 | bSwitch = (Switch) findViewById(R.id.cp_useBrightness_switch); 92 | 93 | appCompatButton = (AppCompatButton) findViewById(R.id.cm_toggle_button); 94 | 95 | restoreLatestSettings(); 96 | 97 | setButtonState(false); 98 | appCompatButton.setOnClickListener(new View.OnClickListener() { 99 | @Override 100 | public void onClick(View view) { 101 | if (!isServiceRunning) { 102 | collectCurrentDarkerSettings(); 103 | setButtonState(true); 104 | isServiceRunning = true; 105 | } else { 106 | ScreenFilterService.removeScreenFilter(); 107 | setButtonState(false); 108 | isServiceRunning = false; 109 | } 110 | darkerNotification.updateStatus(isServiceRunning); 111 | } 112 | }); 113 | 114 | broadcastReceiver = new BroadcastReceiver() { 115 | @Override 116 | public void onReceive(Context context, Intent intent) { 117 | if (DarkerNotification.PRESS_BUTTON.equals(intent.getAction())) { 118 | if (!isServiceRunning) { 119 | collectCurrentDarkerSettings(); 120 | setButtonState(true); 121 | isServiceRunning = true; 122 | } else { 123 | ScreenFilterService.removeScreenFilter(); 124 | setButtonState(false); 125 | isServiceRunning = false; 126 | } 127 | darkerNotification.updateStatus(isServiceRunning); 128 | } 129 | } 130 | }; 131 | 132 | IntentFilter intentFilter = new IntentFilter(); 133 | intentFilter.addAction(DarkerNotification.PRESS_BUTTON); 134 | registerReceiver(broadcastReceiver, intentFilter); 135 | 136 | aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 137 | @Override 138 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 139 | if (isChecked) { 140 | AlphaAnimation alphaAnimation_0 = new AlphaAnimation(0, 1); 141 | alphaAnimation_0.setDuration(300); 142 | colorSeekBar.startAnimation(alphaAnimation_0); 143 | colorSeekBar.setVisibility(View.VISIBLE); 144 | currentDarkerSettings.setUseColor(true); 145 | } else { 146 | AlphaAnimation alphaAnimation_1 = new AlphaAnimation(1, 0); 147 | alphaAnimation_1.setDuration(300); 148 | colorSeekBar.startAnimation(alphaAnimation_1); 149 | colorSeekBar.setVisibility(View.INVISIBLE); 150 | currentDarkerSettings.setUseColor(false); 151 | } 152 | if (isServiceRunning) 153 | collectCurrentDarkerSettings(); 154 | } 155 | }); 156 | 157 | bSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 158 | @Override 159 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 160 | if (isChecked) { 161 | AlphaAnimation alphaAnimation_0 = new AlphaAnimation(0, 1); 162 | alphaAnimation_0.setDuration(300); 163 | circleSeekBar_brightness.startAnimation(alphaAnimation_0); 164 | circleSeekBar_brightness.setVisibility(View.VISIBLE); 165 | brightness_indicator.setText(String.valueOf((int) circleSeekBar_brightness.getProgress())); 166 | currentDarkerSettings.setUseBrightness(true); 167 | } else { 168 | AlphaAnimation alphaAnimation_1 = new AlphaAnimation(1, 0); 169 | alphaAnimation_1.setDuration(300); 170 | circleSeekBar_brightness.startAnimation(alphaAnimation_1); 171 | circleSeekBar_brightness.setVisibility(View.INVISIBLE); 172 | brightness_indicator.setText(R.string.auto_brightness); 173 | currentDarkerSettings.setUseBrightness(true); 174 | } 175 | if (isServiceRunning) 176 | collectCurrentDarkerSettings(); 177 | } 178 | }); 179 | 180 | circleSeekBar_brightness.setOnSeekBarChangeListener(new CircularSeekBar.OnCircularSeekBarChangeListener() { 181 | @Override 182 | public void onProgressChanged(CircularSeekBar circularSeekBar, float progress, boolean fromUser) { 183 | brightness_indicator.setText(String.valueOf((int) progress)); 184 | if (isServiceRunning) 185 | collectCurrentDarkerSettings(); 186 | } 187 | 188 | @Override 189 | public void onStopTrackingTouch(CircularSeekBar seekBar) { 190 | } 191 | 192 | @Override 193 | public void onStartTrackingTouch(CircularSeekBar seekBar) { 194 | } 195 | }); 196 | 197 | circleSeekBar_alpha.setOnSeekBarChangeListener(new CircularSeekBar.OnCircularSeekBarChangeListener() { 198 | @Override 199 | public void onProgressChanged(CircularSeekBar circularSeekBar, float progress, boolean fromUser) { 200 | alpha_indicator.setText(String.valueOf((int) progress)); 201 | if (isServiceRunning) 202 | collectCurrentDarkerSettings(); 203 | } 204 | 205 | @Override 206 | public void onStopTrackingTouch(CircularSeekBar seekBar) { 207 | } 208 | 209 | @Override 210 | public void onStartTrackingTouch(CircularSeekBar seekBar) { 211 | } 212 | }); 213 | 214 | colorSeekBar.setOnColorChangeListener(new ColorSeekBar.OnColorChangeListener() { 215 | @Override 216 | public void onColorChangeListener(int i, int i1, int i2) { 217 | if (isServiceRunning) 218 | collectCurrentDarkerSettings(); 219 | } 220 | }); 221 | } 222 | 223 | @Override 224 | public boolean onCreateOptionsMenu(Menu menu) { 225 | getMenuInflater().inflate(R.menu.menu_main, menu); 226 | return true; 227 | } 228 | 229 | @Override 230 | public boolean onOptionsItemSelected(MenuItem item) { 231 | int id = item.getItemId(); 232 | if (id == R.id.action_about) 233 | startActivity(new Intent(MainActivity.this, AboutActivity.class)); 234 | else if (id == R.id.action_restoreDefaultSettings) { 235 | new AlertDialog.Builder(MainActivity.this) 236 | .setTitle("重置配置面板") 237 | .setMessage("将覆盖您的偏好配置并恢复为推荐配置") 238 | .setPositiveButton("确认重置", new DialogInterface.OnClickListener() { 239 | @Override 240 | public void onClick(DialogInterface dialogInterface, int i) { 241 | currentDarkerSettings = DarkerSettings.getDefaultSettings(); 242 | if (isServiceRunning) { 243 | doRestore(); 244 | collectCurrentDarkerSettings(); 245 | } else 246 | doRestore(); 247 | } 248 | }) 249 | .setNegativeButton("取消", null) 250 | .show(); 251 | } 252 | return super.onOptionsItemSelected(item); 253 | } 254 | 255 | @SuppressLint("RestrictedApi") 256 | private void setButtonState(boolean isChecked) { 257 | if (isChecked) { 258 | final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 259 | ContextCompat.getColor(this, R.color.toggle_button_off), 260 | ContextCompat.getColor(this, R.color.toggle_button_on)); 261 | valueAnimator.setDuration(500); 262 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 263 | @Override 264 | public void onAnimationUpdate(ValueAnimator animation) { 265 | int trans_color = (int) valueAnimator.getAnimatedValue(); 266 | appCompatButton.setSupportBackgroundTintList( 267 | ColorStateList.valueOf(trans_color)); 268 | } 269 | }); 270 | valueAnimator.start(); 271 | appCompatButton.setText(getResources().getString(R.string.is_on)); 272 | } else { 273 | final ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), 274 | ContextCompat.getColor(this, R.color.toggle_button_on), 275 | ContextCompat.getColor(this, R.color.toggle_button_off)); 276 | valueAnimator.setDuration(500); 277 | valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 278 | @Override 279 | public void onAnimationUpdate(ValueAnimator animation) { 280 | int trans_color = (int) valueAnimator.getAnimatedValue(); 281 | appCompatButton.setSupportBackgroundTintList( 282 | ColorStateList.valueOf(trans_color)); 283 | } 284 | }); 285 | valueAnimator.start(); 286 | appCompatButton.setText(getResources().getString(R.string.is_off)); 287 | } 288 | } 289 | 290 | private void doRestore() { 291 | { 292 | boolean isCurrentServiceRunning = false; 293 | if (isServiceRunning) { 294 | isCurrentServiceRunning = true; 295 | isServiceRunning = false; 296 | } 297 | circleSeekBar_brightness.setProgress(currentDarkerSettings.getBrightness() * 100); 298 | circleSeekBar_alpha.setProgress(currentDarkerSettings.getAlpha() * 100); 299 | if (isCurrentServiceRunning) 300 | isServiceRunning = true; 301 | } 302 | 303 | if (aSwitch.isChecked()) { 304 | aSwitch.setChecked(false); 305 | AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0); 306 | alphaAnimation.setDuration(300); 307 | colorSeekBar.startAnimation(alphaAnimation); 308 | colorSeekBar.setVisibility(View.INVISIBLE); 309 | } 310 | 311 | if (bSwitch.isChecked()) { 312 | bSwitch.setChecked(false); 313 | AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0); 314 | alphaAnimation.setDuration(300); 315 | circleSeekBar_brightness.startAnimation(alphaAnimation); 316 | circleSeekBar_brightness.setVisibility(View.INVISIBLE); 317 | brightness_indicator.setText(R.string.auto_brightness); 318 | } 319 | invalidateOptionsMenu(); 320 | } 321 | 322 | private void collectCurrentDarkerSettings() { 323 | currentDarkerSettings.setBrightness(circleSeekBar_brightness.getProgress() / 100); 324 | currentDarkerSettings.setAlpha(circleSeekBar_alpha.getProgress() / 100); 325 | currentDarkerSettings.setUseColor(aSwitch.isChecked()); 326 | currentDarkerSettings.setUseBrightness(bSwitch.isChecked()); 327 | currentDarkerSettings.setColorBarPosition(colorSeekBar.getColorPosition()); 328 | currentDarkerSettings.setColor(colorSeekBar.getColor()); 329 | currentDarkerSettings.saveCurrentSettings(); 330 | 331 | if (isServiceRunning) 332 | ScreenFilterService.updateScreenFilter(currentDarkerSettings); 333 | else { 334 | try { 335 | ScreenFilterService.activateScreenFilter(currentDarkerSettings); 336 | } catch (IllegalStateException ignored) { 337 | } 338 | } 339 | } 340 | 341 | private void restoreLatestSettings() { 342 | currentDarkerSettings = DarkerSettings.getCurrentSettings(); 343 | circleSeekBar_brightness.setProgress(currentDarkerSettings.getBrightness() * 100); 344 | circleSeekBar_alpha.setProgress(currentDarkerSettings.getAlpha() * 100); 345 | brightness_indicator.setText(String.valueOf((int) circleSeekBar_brightness.getProgress())); 346 | alpha_indicator.setText(String.valueOf((int) circleSeekBar_alpha.getProgress())); 347 | if (currentDarkerSettings.isUseColor()) { 348 | aSwitch.setChecked(true); 349 | colorSeekBar.setVisibility(View.VISIBLE); 350 | } else { 351 | aSwitch.setChecked(false); 352 | colorSeekBar.setVisibility(View.INVISIBLE); 353 | } 354 | if (currentDarkerSettings.isUseBrightness()) { 355 | bSwitch.setChecked(true); 356 | circleSeekBar_brightness.setVisibility(View.VISIBLE); 357 | } else { 358 | bSwitch.setChecked(false); 359 | circleSeekBar_brightness.setVisibility(View.INVISIBLE); 360 | brightness_indicator.setText(R.string.auto_brightness); 361 | } 362 | colorSeekBar.setColorBarValue((int) currentDarkerSettings.getColorBarPosition()); 363 | } 364 | 365 | @Override 366 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 367 | if (requestCode == 0) { 368 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 369 | if (Settings.canDrawOverlays(this)) 370 | prepareForService(); 371 | else { 372 | doCleanBeforeExit(); 373 | finish(); 374 | System.exit(0); 375 | } 376 | } 377 | } 378 | super.onActivityResult(requestCode, resultCode, data); 379 | } 380 | 381 | @Override 382 | public boolean onKeyDown(int keyCode, KeyEvent event) { 383 | if (keyCode == KeyEvent.KEYCODE_BACK) 384 | moveTaskToBack(true); 385 | return super.onKeyDown(keyCode, event); 386 | } 387 | 388 | private void checkPermissions() { 389 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 390 | if (!Settings.canDrawOverlays(getApplicationContext())) { 391 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); 392 | intent.setData(Uri.parse("package:" + getPackageName())); 393 | startActivityForResult(intent, 0); 394 | } else 395 | prepareForService(); 396 | } else 397 | prepareForService(); 398 | } 399 | 400 | private void prepareForService() { 401 | intent = new Intent(getApplicationContext(), ScreenFilterService.class); 402 | /*Display display = getWindowManager().getDefaultDisplay(); 403 | Point point = new Point(); 404 | display.getSize(point); 405 | intent.putExtra("height",point.y);*/ 406 | startService(intent); 407 | } 408 | 409 | private void doCleanBeforeExit() { 410 | try { 411 | darkerNotification.removeNotification(); 412 | unregisterReceiver(broadcastReceiver); 413 | stopService(intent); 414 | } catch (Exception ignored) { 415 | } 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /app/src/main/java/com/amastigote/darker/model/DarkerNotification.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker.model; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.support.v4.app.NotificationCompat; 9 | 10 | import com.amastigote.darker.R; 11 | import com.amastigote.darker.activity.MainActivity; 12 | 13 | public class DarkerNotification { 14 | private static NotificationManager notificationManager; 15 | private static NotificationCompat.Builder builder; 16 | private final static String IS_ON = "滤镜已激活 (•̀ᴗ•́)و"; 17 | private final static String IS_OFF = "滤镜未激活 (˘•_•˘)"; 18 | private final static String CLICK_TO_ACTIVATE = "轻触可启用"; 19 | private final static String CLICK_TO_DEACTIVATE = "轻触可关闭"; 20 | private final static String OPEN_PANEL = "设置面板"; 21 | public final static String PRESS_BUTTON = "PRESS_BUTTON"; 22 | 23 | public DarkerNotification(Context context) { 24 | notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 25 | builder = new NotificationCompat.Builder(context) 26 | .setSmallIcon(R.mipmap.night_128) 27 | .setAutoCancel(false); 28 | PendingIntent settingsPendingIntent = PendingIntent.getActivity( 29 | context, 30 | 0, 31 | new Intent(context, MainActivity.class), 32 | 0); 33 | builder.addAction(R.mipmap.ic_settings_white_24dp, OPEN_PANEL, settingsPendingIntent); 34 | PendingIntent changeStatusPendingIntent = PendingIntent.getBroadcast( 35 | context, 36 | 0, 37 | new Intent(PRESS_BUTTON), 38 | 0); 39 | builder.setContentIntent(changeStatusPendingIntent); 40 | } 41 | 42 | public void updateStatus(boolean isRunning) { 43 | if (isRunning) { 44 | builder.setContentTitle(IS_ON) 45 | .setContentText(CLICK_TO_DEACTIVATE); 46 | } else { 47 | builder.setContentTitle(IS_OFF) 48 | .setContentText(CLICK_TO_ACTIVATE); 49 | } 50 | Notification notification = builder.build(); 51 | notification.flags |= Notification.FLAG_NO_CLEAR; 52 | notificationManager.notify(R.string.app_name, notification); 53 | } 54 | 55 | public void removeNotification() { 56 | notificationManager.cancel(R.string.app_name); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/amastigote/darker/model/DarkerSettings.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker.model; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.view.WindowManager; 7 | 8 | public class DarkerSettings { 9 | private float brightness; 10 | private float alpha; 11 | private boolean useColor; 12 | private boolean useBrightness; 13 | private boolean useBar; 14 | private float colorBarPosition; 15 | private int color; 16 | 17 | @SuppressLint("StaticFieldLeak") 18 | private static Context context = null; 19 | private static SharedPreferences sharedPreferences_default = null; 20 | private static SharedPreferences sharedPreferences_current = null; 21 | private static final String BRIGHTNESS = "BRIGHTNESS"; 22 | private static final String ALPHA = "ALPHA"; 23 | private static final String COLOR_BAR_POSITION = "COLOR_BAR_POSITION"; 24 | private static final String USE_COLOR = "USE_COLOR"; 25 | private static final String USE_BRIGHTNESS = "USE_BRIGHTNESS"; 26 | private static final float ALPHA_DEFAULT = 0.4F; 27 | private static final float COLOR_BAR_POSITION_DEFAULT = 0.0F; 28 | private static final boolean USE_STH_DEFAULT = false; 29 | 30 | public static void initializeContext(Context context) { 31 | DarkerSettings.context = context; 32 | initializeSharedPreferences(); 33 | } 34 | 35 | private static void initializeSharedPreferences() { 36 | sharedPreferences_default = DarkerSettings.context 37 | .getSharedPreferences("user_settings_default", 38 | Context.MODE_PRIVATE); 39 | sharedPreferences_current = DarkerSettings.context 40 | .getSharedPreferences("user_settings_current", 41 | Context.MODE_PRIVATE); 42 | } 43 | 44 | public DarkerSettings() { 45 | } 46 | 47 | public static DarkerSettings getDefaultSettings() { 48 | return getSettings(sharedPreferences_default); 49 | } 50 | 51 | public static DarkerSettings getCurrentSettings() { 52 | return getSettings(sharedPreferences_current); 53 | } 54 | 55 | private static DarkerSettings getSettings(SharedPreferences sharedPreferences) { 56 | DarkerSettings thisDarkerSettings = new DarkerSettings(); 57 | 58 | float thisBrightness = sharedPreferences.getFloat(BRIGHTNESS, 59 | WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF); 60 | float thisAlpha = sharedPreferences.getFloat(ALPHA, 61 | ALPHA_DEFAULT); 62 | float thisColorBarPosition = sharedPreferences.getFloat(COLOR_BAR_POSITION, 63 | COLOR_BAR_POSITION_DEFAULT); 64 | boolean thisUseColor = sharedPreferences.getBoolean(USE_COLOR, 65 | USE_STH_DEFAULT); 66 | boolean thisUseBrightness = sharedPreferences.getBoolean(USE_BRIGHTNESS, 67 | USE_STH_DEFAULT); 68 | 69 | thisDarkerSettings.setBrightness(thisBrightness); 70 | thisDarkerSettings.setAlpha(thisAlpha); 71 | thisDarkerSettings.setColorBarPosition(thisColorBarPosition); 72 | thisDarkerSettings.setUseColor(thisUseColor); 73 | thisDarkerSettings.setUseBrightness(thisUseBrightness); 74 | 75 | return thisDarkerSettings; 76 | } 77 | 78 | public void setBrightness(float brightness) { 79 | this.brightness = brightness; 80 | } 81 | 82 | public void setAlpha(float alpha) { 83 | this.alpha = alpha; 84 | } 85 | 86 | public float getColorBarPosition() { 87 | return colorBarPosition; 88 | } 89 | 90 | public void setColorBarPosition(float colorBarPosition) { 91 | this.colorBarPosition = colorBarPosition; 92 | } 93 | 94 | public void setUseColor(boolean useColor) { 95 | this.useColor = useColor; 96 | } 97 | 98 | public float getBrightness() { 99 | return brightness; 100 | } 101 | 102 | public float getAlpha() { 103 | return alpha; 104 | } 105 | 106 | public boolean isUseColor() { 107 | return useColor; 108 | } 109 | 110 | public int getColor() { 111 | return color; 112 | } 113 | 114 | public void setColor(int color) { 115 | this.color = color; 116 | } 117 | 118 | public boolean isUseBrightness() { 119 | return useBrightness; 120 | } 121 | 122 | public void setUseBrightness(boolean useBrightness) { 123 | this.useBrightness = useBrightness; 124 | } 125 | 126 | public void saveCurrentSettings() { 127 | SharedPreferences.Editor editor = sharedPreferences_current.edit(); 128 | editor.putFloat(BRIGHTNESS, brightness); 129 | editor.putFloat(ALPHA, alpha); 130 | editor.putFloat(COLOR_BAR_POSITION, colorBarPosition); 131 | editor.putBoolean(USE_BRIGHTNESS, useBrightness); 132 | editor.putBoolean(USE_COLOR, useColor); 133 | editor.apply(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/amastigote/darker/service/ScreenFilterService.java: -------------------------------------------------------------------------------- 1 | package com.amastigote.darker.service; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Service; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.graphics.Color; 8 | import android.graphics.PixelFormat; 9 | import android.os.IBinder; 10 | import android.support.annotation.Nullable; 11 | import android.util.DisplayMetrics; 12 | import android.view.LayoutInflater; 13 | import android.view.WindowManager; 14 | import android.widget.LinearLayout; 15 | 16 | import com.amastigote.darker.R; 17 | import com.amastigote.darker.model.DarkerSettings; 18 | 19 | //import android.support.v7.app.AppCompatActivity; 20 | //import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompatApi21.getWindow; 21 | 22 | public class ScreenFilterService extends Service { 23 | @SuppressLint("StaticFieldLeak") 24 | static LinearLayout linearLayout; 25 | static WindowManager.LayoutParams layoutParams; 26 | static WindowManager windowManager; 27 | 28 | @Override 29 | public void onCreate() { 30 | super.onCreate(); 31 | createScreenFilter(); 32 | } 33 | 34 | @Override 35 | public int onStartCommand(Intent intent, int flags, int startId) { 36 | return START_STICKY; 37 | } 38 | 39 | @Override 40 | public void onDestroy() { 41 | super.onDestroy(); 42 | try { 43 | windowManager.removeViewImmediate(linearLayout); 44 | } catch (Exception ignored) { 45 | } 46 | } 47 | 48 | @Nullable 49 | @Override 50 | public IBinder onBind(Intent intent) { 51 | return null; 52 | } 53 | 54 | @SuppressWarnings(value = "all") 55 | private void createScreenFilter() { 56 | WindowManager windowManager_tmp = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 57 | layoutParams = new WindowManager.LayoutParams(); 58 | windowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE); 59 | layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; 60 | 61 | int temp = getNavigationBarHeight(getApplicationContext()); 62 | DisplayMetrics metrics = new DisplayMetrics(); 63 | windowManager_tmp.getDefaultDisplay().getRealMetrics(metrics); 64 | layoutParams.width = metrics.heightPixels + temp; 65 | layoutParams.height = metrics.heightPixels + temp; 66 | 67 | layoutParams.format = PixelFormat.TRANSLUCENT; 68 | 69 | LayoutInflater layoutInflater = LayoutInflater.from(getApplication()); 70 | linearLayout = (LinearLayout) layoutInflater.inflate(R.layout.screen_filter, null); 71 | } 72 | 73 | public static int getNavigationBarHeight(Context context) { 74 | int resourceId = 0; 75 | int rid = context.getResources().getIdentifier("config_showNavigationBar", "bool", "android"); 76 | if (rid != 0) { 77 | resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android"); 78 | return context.getResources().getDimensionPixelSize(resourceId); 79 | } else 80 | return 0; 81 | } 82 | 83 | public static void updateScreenFilter(DarkerSettings darkerSettings) { 84 | updateThisSettings(darkerSettings); 85 | windowManager.updateViewLayout(linearLayout, layoutParams); 86 | } 87 | 88 | public static void activateScreenFilter(DarkerSettings darkerSettings) { 89 | updateThisSettings(darkerSettings); 90 | windowManager.addView(linearLayout, layoutParams); 91 | } 92 | 93 | private static void updateThisSettings(DarkerSettings darkerSettings) { 94 | if (darkerSettings.isUseBrightness()) 95 | layoutParams.screenBrightness = darkerSettings.getBrightness(); 96 | else 97 | layoutParams.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; 98 | 99 | layoutParams.alpha = darkerSettings.getAlpha(); 100 | 101 | if (darkerSettings.isUseColor()) 102 | linearLayout.setBackgroundColor(darkerSettings.getColor()); 103 | else 104 | linearLayout.setBackgroundColor(Color.BLACK); 105 | 106 | layoutParams.flags = 107 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 108 | | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 109 | | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 110 | // | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 111 | | WindowManager.LayoutParams.FLAG_FULLSCREEN 112 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 113 | } 114 | 115 | public static void removeScreenFilter() { 116 | windowManager.removeViewImmediate(linearLayout); 117 | } 118 | /* 119 | protected void hideBottomUIMenu() { 120 | if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api 121 | View v = this.getWindow().getDecorView(); 122 | v.setSystemUiVisibility(View.GONE); 123 | } else if (Build.VERSION.SDK_INT >= 19) { 124 | //for new api versions. 125 | View decorView = getWindow().getDecorView(); 126 | int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 127 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN; 128 | decorView.setSystemUiVisibility(uiOptions); 129 | } 130 | }*/ 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/com/rtugeek/android/colorseekbar/ColorSeekBar.java: -------------------------------------------------------------------------------- 1 | package com.rtugeek.android.colorseekbar; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.LinearGradient; 9 | import android.graphics.Paint; 10 | import android.graphics.Rect; 11 | import android.graphics.RectF; 12 | import android.graphics.Shader; 13 | import android.util.AttributeSet; 14 | import android.view.MotionEvent; 15 | import android.view.View; 16 | 17 | import com.amastigote.darker.R; 18 | 19 | @SuppressWarnings(value = "all") 20 | public class ColorSeekBar extends View { 21 | private int mBackgroundColor = 0xffffffff; 22 | private int[] mColors = new int[]{0xFF000000, 0xFF9900FF, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF, 0xFFFF0000, 0xFFFF00FF, 0xFFFF6600, 0xFFFFFF00, 0xFFFFFFFF, 0xFF000000}; 23 | private int c0, c1, mAlpha, mRed, mGreen, mBlue; 24 | private float x, y; 25 | private OnColorChangeListener mOnColorChangeLister; 26 | private Context mContext; 27 | private boolean mIsShowAlphaBar = false; 28 | private Bitmap mTransparentBitmap; 29 | private boolean mMovingColorBar; 30 | private boolean mMovingAlphaBar; 31 | private Rect mColorRect; 32 | private int mThumbHeight = 20; 33 | private int mBarHeight = 2; 34 | private LinearGradient mColorGradient; 35 | private Paint mColorRectPaint; 36 | private int realLeft; 37 | private int realRight; 38 | private int realTop; 39 | private int realBottom; 40 | private int mBarWidth; 41 | private int mMaxValue; 42 | private Rect mAlphaRect; 43 | private int mColorBarValue; 44 | private int mAlphaBarValue; 45 | private float mThumbRadius; 46 | private int mBarMargin = 5; 47 | private int mPaddingSize; 48 | private int mViewWidth; 49 | private int mViewHeight; 50 | 51 | public ColorSeekBar(Context context) { 52 | super(context); 53 | init(context, null, 0, 0); 54 | } 55 | 56 | public ColorSeekBar(Context context, AttributeSet attrs) { 57 | super(context, attrs); 58 | init(context, attrs, 0, 0); 59 | } 60 | 61 | public ColorSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { 62 | super(context, attrs, defStyleAttr); 63 | init(context, attrs, defStyleAttr, 0); 64 | } 65 | 66 | // @TargetApi(Build.VERSION_CODES.LOLLIPOP) 67 | // public ColorSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 68 | // super(context, attrs, defStyleAttr, defStyleRes); 69 | // init(context, attrs, defStyleAttr, defStyleRes); 70 | // } 71 | 72 | protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 73 | applyStyle(context, attrs, defStyleAttr, defStyleRes); 74 | } 75 | 76 | // public void applyStyle(int resId){ 77 | // applyStyle(getContext(), null, 0, resId); 78 | // } 79 | 80 | @Override 81 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 82 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 83 | mViewWidth = widthMeasureSpec; 84 | mViewHeight = heightMeasureSpec; 85 | 86 | int speMode = MeasureSpec.getMode(heightMeasureSpec); 87 | if (speMode == MeasureSpec.AT_MOST || speMode == MeasureSpec.UNSPECIFIED) { 88 | if (mIsShowAlphaBar) { 89 | setMeasuredDimension(mViewWidth, mThumbHeight * 2 + mBarHeight * 2 + mBarMargin); 90 | } else { 91 | setMeasuredDimension(mViewWidth, mThumbHeight + mBarHeight); 92 | } 93 | } 94 | } 95 | 96 | 97 | protected void applyStyle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 98 | mContext = context; 99 | //get attributes 100 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ColorSeekBar, defStyleAttr, defStyleRes); 101 | int colorsId = a.getResourceId(R.styleable.ColorSeekBar_colors, 0); 102 | mMaxValue = a.getInteger(R.styleable.ColorSeekBar_maxValue, 100); 103 | mColorBarValue = a.getInteger(R.styleable.ColorSeekBar_colorBarValue, 0); 104 | mAlphaBarValue = a.getInteger(R.styleable.ColorSeekBar_alphaBarValue, 0); 105 | mIsShowAlphaBar = a.getBoolean(R.styleable.ColorSeekBar_showAlphaBar, false); 106 | mBackgroundColor = a.getColor(R.styleable.ColorSeekBar_bgColor, Color.TRANSPARENT); 107 | mBarHeight = (int) a.getDimension(R.styleable.ColorSeekBar_barHeight, (float) dp2px(2)); 108 | mThumbHeight = (int) a.getDimension(R.styleable.ColorSeekBar_thumbHeight, (float) dp2px(30)); 109 | mBarMargin = (int) a.getDimension(R.styleable.ColorSeekBar_barMargin, (float) dp2px(5)); 110 | a.recycle(); 111 | 112 | if (colorsId != 0) mColors = getColorsById(colorsId); 113 | 114 | setBackgroundColor(mBackgroundColor); 115 | 116 | init(); 117 | 118 | //init color 119 | pickColor(mColorBarValue); 120 | setAlphaValue(); 121 | 122 | } 123 | 124 | private int[] getColorsById(int id) { 125 | if (isInEditMode()) { 126 | String[] s = mContext.getResources().getStringArray(id); 127 | int[] colors = new int[s.length]; 128 | for (int j = 0; j < s.length; j++) { 129 | colors[j] = Color.parseColor(s[j]); 130 | } 131 | return colors; 132 | } else { 133 | TypedArray typedArray = mContext.getResources().obtainTypedArray(id); 134 | int[] colors = new int[typedArray.length()]; 135 | for (int j = 0; j < typedArray.length(); j++) { 136 | colors[j] = typedArray.getColor(j, Color.BLACK); 137 | } 138 | typedArray.recycle(); 139 | return colors; 140 | } 141 | } 142 | 143 | private void init() { 144 | //init l r t b 145 | realLeft = getPaddingLeft() + mPaddingSize; 146 | realRight = getWidth() - getPaddingRight() - mPaddingSize; 147 | realTop = getPaddingTop() + mPaddingSize; 148 | realBottom = getHeight() - getPaddingBottom() - mPaddingSize; 149 | 150 | //init size 151 | mThumbRadius = mThumbHeight / 2; 152 | mPaddingSize = (int) mThumbRadius; 153 | mBarWidth = realRight - realLeft; 154 | 155 | //init rect 156 | mColorRect = new Rect(realLeft, realTop, realRight, realTop + mBarHeight); 157 | 158 | //init paint 159 | mColorGradient = new LinearGradient(0, 0, mColorRect.width(), 0, mColors, null, Shader.TileMode.MIRROR); 160 | mColorRectPaint = new Paint(); 161 | mColorRectPaint.setShader(mColorGradient); 162 | mColorRectPaint.setAntiAlias(true); 163 | 164 | } 165 | 166 | @Override 167 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 168 | super.onSizeChanged(w, h, oldw, oldh); 169 | mTransparentBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); 170 | mTransparentBitmap.eraseColor(Color.TRANSPARENT); 171 | } 172 | 173 | 174 | // public ColorSeekBar(Context context, float x1, float x2) { 175 | // super(context); 176 | // } 177 | 178 | @Override 179 | protected void onDraw(Canvas canvas) { 180 | init(); 181 | float colorPosition = (float) mColorBarValue / mMaxValue * mBarWidth; 182 | 183 | Paint colorPaint = new Paint(); 184 | colorPaint.setAntiAlias(true); 185 | colorPaint.setColor(pickColor(colorPosition)); 186 | // int[] toAlpha=new int[]{Color.argb(255, mRed, mGreen, mBlue),Color.argb(0, mRed, mGreen, mBlue)}; 187 | //clear 188 | canvas.drawBitmap(mTransparentBitmap, 0, 0, null); 189 | 190 | //draw color bar 191 | // canvas.drawRect(mColorRect, mColorRectPaint); 192 | canvas.drawRoundRect(new RectF(mColorRect), mBarHeight / 2, mBarHeight / 2, mColorRectPaint); 193 | 194 | //draw color bar thumb 195 | float thumbX = colorPosition + realLeft; 196 | float thumbY = mColorRect.top + mColorRect.height() / 2; 197 | canvas.drawCircle(thumbX, thumbY, mBarHeight, colorPaint); 198 | 199 | // //draw color bar thumb radial gradient shader 200 | // RadialGradient thumbShader = new RadialGradient(thumbX, thumbY, mThumbRadius, toAlpha, null, Shader.TileMode.MIRROR); 201 | // Paint thumbGradientPaint = new Paint(); 202 | // thumbGradientPaint.setAntiAlias(true); 203 | // thumbGradientPaint.setShader(thumbShader); 204 | // canvas.drawCircle(thumbX, thumbY, mThumbHeight / 2, thumbGradientPaint); 205 | 206 | // if(mIsShowAlphaBar){ 207 | // 208 | // //init rect 209 | // int top = (int)(mThumbHeight+ mThumbRadius + mBarHeight + mBarMargin); 210 | // mAlphaRect = new Rect(realLeft,top,realRight,top + mBarHeight); 211 | // 212 | // //draw alpha bar 213 | // Paint alphaBarPaint = new Paint(); 214 | // alphaBarPaint.setAntiAlias(true); 215 | // LinearGradient alphaBarShader = new LinearGradient(0, 0, mAlphaRect.width(), 0, toAlpha, null, Shader.TileMode.MIRROR); 216 | // alphaBarPaint.setShader(alphaBarShader); 217 | // canvas.drawRect(mAlphaRect,alphaBarPaint); 218 | // 219 | // //draw alpha bar thumb 220 | // float alphaPosition = (float) mAlphaBarValue / 255 * mBarWidth; 221 | // float alphaThumbX = alphaPosition + realLeft; 222 | // float alphaThumbY = mAlphaRect.top + mAlphaRect.height() / 2; 223 | // canvas.drawCircle(alphaThumbX, alphaThumbY, mBarHeight / 2 + 5, colorPaint); 224 | // 225 | // //draw alpha bar thumb radial gradient shader 226 | // RadialGradient alphaThumbShader = new RadialGradient(alphaThumbX, alphaThumbY, mThumbRadius, toAlpha, null, Shader.TileMode.MIRROR); 227 | // Paint alphaThumbGradientPaint = new Paint(); 228 | // alphaThumbGradientPaint.setAntiAlias(true); 229 | // alphaThumbGradientPaint.setShader(alphaThumbShader); 230 | // canvas.drawCircle(alphaThumbX,alphaThumbY, mThumbHeight/2, alphaThumbGradientPaint); 231 | // } 232 | 233 | super.onDraw(canvas); 234 | } 235 | 236 | 237 | @Override 238 | public boolean onTouchEvent(MotionEvent event) { 239 | x = event.getX(); 240 | y = event.getY(); 241 | switch (event.getAction()) { 242 | case MotionEvent.ACTION_DOWN: 243 | if (isOnBar(mColorRect, x, y)) { 244 | mMovingColorBar = true; 245 | } else if (mIsShowAlphaBar) { 246 | if (isOnBar(mAlphaRect, x, y)) { 247 | mMovingAlphaBar = true; 248 | } 249 | } 250 | break; 251 | case MotionEvent.ACTION_MOVE: 252 | getParent().requestDisallowInterceptTouchEvent(true); 253 | if (mMovingColorBar) { 254 | float value = (x - realLeft) / mBarWidth * mMaxValue; 255 | mColorBarValue = (int) value; 256 | if (mColorBarValue < 0) mColorBarValue = 0; 257 | if (mColorBarValue > mMaxValue) mColorBarValue = mMaxValue; 258 | } else if (mIsShowAlphaBar) { 259 | if (mMovingAlphaBar) { 260 | float value = (x - realLeft) / mBarWidth * 255; 261 | mAlphaBarValue = (int) value; 262 | if (mAlphaBarValue < 0) mAlphaBarValue = 0; 263 | if (mAlphaBarValue > 255) mAlphaBarValue = 255; 264 | setAlphaValue(); 265 | } 266 | } 267 | if (mOnColorChangeLister != null && (mMovingAlphaBar || mMovingColorBar)) 268 | mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue, getColor()); 269 | invalidate(); 270 | break; 271 | case MotionEvent.ACTION_UP: 272 | mMovingColorBar = false; 273 | mMovingAlphaBar = false; 274 | break; 275 | } 276 | return true; 277 | } 278 | 279 | private boolean isOnBar(Rect r, float x, float y) { 280 | return r.left - mThumbRadius < x && x < r.right + mThumbRadius && r.top - mThumbRadius < y && y < r.bottom + mThumbRadius; 281 | } 282 | 283 | private int pickColor(float position) { 284 | float unit = position / mBarWidth; 285 | if (unit <= 0.0) 286 | return mColors[0]; 287 | 288 | if (unit >= 1) 289 | return mColors[mColors.length - 1]; 290 | 291 | float colorPosition = unit * (mColors.length - 1); 292 | int i = (int) colorPosition; 293 | colorPosition -= i; 294 | c0 = mColors[i]; 295 | c1 = mColors[i + 1]; 296 | // mAlpha = mix(Color.alpha(c0), Color.alpha(c1), colorPosition); 297 | mRed = mix(Color.red(c0), Color.red(c1), colorPosition); 298 | mGreen = mix(Color.green(c0), Color.green(c1), colorPosition); 299 | mBlue = mix(Color.blue(c0), Color.blue(c1), colorPosition); 300 | return Color.rgb(mRed, mGreen, mBlue); 301 | } 302 | 303 | private int mix(int start, int end, float position) { 304 | return start + Math.round(position * (end - start)); 305 | } 306 | 307 | public int getColor() { 308 | if (mIsShowAlphaBar) return Color.argb(mAlpha, mRed, mGreen, mBlue); 309 | return Color.rgb(mRed, mGreen, mBlue); 310 | } 311 | 312 | // 313 | // public int getAlphaValue(){ 314 | // return mAlpha; 315 | // } 316 | // 317 | // 318 | // /** 319 | // * @return color with alpha value 320 | // */ 321 | // public int getColorWithAlpha(){ 322 | // return Color.argb(mAlpha, mRed, mGreen, mBlue); 323 | // } 324 | 325 | 326 | public interface OnColorChangeListener { 327 | /** 328 | * @param colorBarValue between 0-maxValue 329 | * @param alphaValue between 0-255 330 | * @param color return the color contains alpha value whether showAlphaBar is true or without alpha value 331 | */ 332 | void onColorChangeListener(int colorBarValue, int alphaValue, int color); 333 | } 334 | 335 | public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener) { 336 | this.mOnColorChangeLister = onColorChangeListener; 337 | } 338 | 339 | 340 | public int dp2px(float dpValue) { 341 | final float scale = mContext.getResources().getDisplayMetrics().density; 342 | return (int) (dpValue * scale + 0.5f); 343 | } 344 | 345 | // /** 346 | // * Set colors by resource id. The resource's type must be color 347 | // * @param resId 348 | // */ 349 | // public void setColors(int resId){ 350 | // setColors(getColorsById(resId)); 351 | // } 352 | // 353 | // public void setColors(int[] colors){ 354 | // mColors = colors; 355 | // invalidate(); 356 | // setAlphaValue(); 357 | // if(mOnColorChangeLister != null) 358 | // mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor()); 359 | // } 360 | // 361 | // public int[] getColors(){ 362 | // return mColors; 363 | // } 364 | // 365 | // public boolean isShowAlphaBar(){ 366 | // return mIsShowAlphaBar; 367 | // } 368 | // 369 | // private void refreshLayoutParams(){ 370 | // mThumbHeight = mThumbHeight < 2? 2:mThumbHeight; 371 | // mBarHeight = mBarHeight < 2? 2:mBarHeight; 372 | // 373 | // int singleHeight = mThumbHeight + mBarHeight; 374 | // int doubleHeight = mThumbHeight *2 + mBarHeight * 2 + mBarMargin; 375 | // 376 | // if(getLayoutParams().height == -2) { 377 | // if(mIsShowAlphaBar){ 378 | // getLayoutParams().height = doubleHeight; 379 | // setLayoutParams(getLayoutParams()); 380 | // }else{ 381 | // getLayoutParams().height = singleHeight ; 382 | // setLayoutParams(getLayoutParams()); 383 | // } 384 | // }else if (getLayoutParams().height >= 0){ 385 | // if(mIsShowAlphaBar){ 386 | // getLayoutParams().height = doubleHeight; 387 | // setLayoutParams(getLayoutParams()); 388 | // }else{ 389 | // getLayoutParams().height = singleHeight; 390 | // setLayoutParams(getLayoutParams()); 391 | // } 392 | // } 393 | // } 394 | // 395 | // public void setShowAlphaBar(boolean show){ 396 | // mIsShowAlphaBar = show; 397 | // refreshLayoutParams(); 398 | // invalidate(); 399 | // if(mOnColorChangeLister != null) 400 | // mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue,getColor()); 401 | // } 402 | // 403 | // /** 404 | // * @param dp 405 | // */ 406 | // public void setBarHeight(float dp){ 407 | // mBarHeight = dp2px(dp); 408 | // refreshLayoutParams(); 409 | // invalidate(); 410 | // } 411 | // 412 | // /** 413 | // * @param px 414 | // */ 415 | // public void setBarHeightPx(int px){ 416 | // mBarHeight = px; 417 | // refreshLayoutParams(); 418 | // invalidate(); 419 | // } 420 | 421 | private void setAlphaValue() { 422 | mAlpha = 255 - mAlphaBarValue; 423 | } 424 | 425 | // public void setAlphaBarValue(int value) { 426 | // this.mAlphaBarValue = value; 427 | // setAlphaValue(); 428 | // invalidate(); 429 | // } 430 | // 431 | // public int getMaxValue() { 432 | // return mMaxValue; 433 | // } 434 | // 435 | // public void setMaxValue(int value) { 436 | // this.mMaxValue = value; 437 | // invalidate(); 438 | // } 439 | // 440 | // /** 441 | // * set margin between bars 442 | // * @param mBarMargin 443 | // */ 444 | // public void setBarMargin(float mBarMargin) { 445 | // this.mBarMargin = dp2px(mBarMargin); 446 | // refreshLayoutParams(); 447 | // invalidate(); 448 | // } 449 | // 450 | // /** 451 | // * set margin between bars 452 | // * @param mBarMargin 453 | // */ 454 | // public void setBarMarginPx(int mBarMargin) { 455 | // this.mBarMargin = mBarMargin; 456 | // refreshLayoutParams(); 457 | // invalidate(); 458 | // } 459 | 460 | public void setColorBarValue(int value) { 461 | this.mColorBarValue = value; 462 | invalidate(); 463 | if (mOnColorChangeLister != null) 464 | mOnColorChangeLister.onColorChangeListener(mColorBarValue, mAlphaBarValue, getColor()); 465 | } 466 | 467 | // /** 468 | // * set thumb's height by dpi 469 | // * @param dp 470 | // */ 471 | // public void setThumbHeight(float dp) { 472 | // this.mThumbHeight = dp2px(dp); 473 | // refreshLayoutParams(); 474 | // invalidate(); 475 | // } 476 | // 477 | // /** 478 | // * set thumb's height by pixels 479 | // * @param px 480 | // */ 481 | // public void setThumbHeightPx(int px) { 482 | // this.mThumbHeight = px; 483 | // refreshLayoutParams(); 484 | // invalidate(); 485 | // } 486 | // 487 | // public int getBarHeight() { 488 | // return mBarHeight; 489 | // } 490 | // 491 | // public int getThumbHeight() { 492 | // return mThumbHeight; 493 | // } 494 | // 495 | // public int getBarMargin() { 496 | // return mBarMargin; 497 | // } 498 | // 499 | public float getColorPosition() { 500 | return mColorBarValue; 501 | } 502 | 503 | 504 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_turn_on_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_checked_toggle_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_text_tag.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_unchecked_toggle_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 20 | 21 | 28 | 29 | 35 | 36 | 42 | 43 | 48 | 49 | 54 | 55 | 60 | 61 | 62 | 67 | 68 | 73 | 74 | 79 | 80 | 81 | 87 | 88 |