編輯:關於android開發
項目地址:https://github.com/hwding/make-it-darker
歡迎star、fork以及一切建議和意見!
在完全黑暗中看手機眼睛會非常容易疲勞,即使將手機亮度調整到最低也會讓自己在完全適應後感到非常刺眼。
利用android的懸浮窗將半透明的黑色layout覆蓋在屏幕上進一步降低亮度。
同時通過改變濾鏡顏色可以過濾藍光等等。
出於自身需求給自己寫了一個小玩意~
效果:
懸浮窗作為服務啟動:
1 package com.amastigote.darker.service; 2 3 import android.app.Service; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.os.IBinder; 7 import android.support.annotation.Nullable; 8 import android.view.LayoutInflater; 9 import android.view.WindowManager; 10 import android.widget.LinearLayout; 11 import com.amastigote.darker.R; 12 import com.amastigote.darker.model.DarkerSettings; 13 14 public class ScreenFilterService extends Service{ 15 static LinearLayout linearLayout; 16 static WindowManager.LayoutParams layoutParams; 17 static WindowManager windowManager; 18 19 @Override 20 public void onCreate() { 21 super.onCreate(); 22 createScreenFilter(); 23 } 24 25 @Override 26 public void onDestroy() { 27 super.onDestroy(); 28 if (linearLayout != null) 29 windowManager.removeView(linearLayout); 30 } 31 32 @Nullable 33 @Override 34 public IBinder onBind(Intent intent) { 35 return null; 36 } 37 38 @SuppressWarnings(value = "all") 39 private void createScreenFilter() { 40 layoutParams = new WindowManager.LayoutParams(); 41 windowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE); 42 layoutParams.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; 43 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 44 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 45 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 46 layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; 47 layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; 48 49 LayoutInflater layoutInflater = LayoutInflater.from(getApplication()); 50 linearLayout = (LinearLayout) layoutInflater.inflate(R.layout.screen_filter, null); 51 windowManager.addView(linearLayout, layoutParams); 52 removeScreenFilter(); 53 } 54 55 public static void updateScreenFilter(DarkerSettings darkerSettings) { 56 layoutParams.screenBrightness = darkerSettings.getBrightness(); 57 layoutParams.alpha = darkerSettings.getAlpha(); 58 windowManager.updateViewLayout(linearLayout, layoutParams); 59 } 60 61 public static void removeScreenFilter() { 62 layoutParams.screenBrightness = DarkerSettings.BRIGHTNESS_AUTO; 63 layoutParams.alpha = DarkerSettings.ALPHA_MINIMUM; 64 windowManager.updateViewLayout(linearLayout, layoutParams); 65 } 66 } ScreenFilterService.java此處應注意,給layoutParams設置屬性type時需要用到類型TYPE_PRIORITY_PHONE,可以覆蓋一切屏幕上的內容。
關於此項類型需要特定的permission申請,此處不表,見下。
screenBrightness為懸浮窗自身亮度,這裡默認為最低0.0F
alpha為懸浮窗layout的透明度,默認為0.4F,最高設定為0.8F防止全黑導致無法操作。
update...()方法用於讀入用戶在app控制面版上的配置並更新filter
remove...()方法並沒有移除filter而是將filter的亮度恢復為自動並設置為全透明,方便下一次啟動filter。
1 package com.amastigote.darker.activity; 2 3 import android.content.Intent; 4 import android.net.Uri; 5 import android.os.Build; 6 import android.os.Bundle; 7 import android.provider.Settings; 8 import android.support.v7.app.AppCompatActivity; 9 import android.support.v7.widget.Toolbar; 10 import android.view.Menu; 11 import android.view.MenuItem; 12 import android.view.View; 13 import android.view.animation.AlphaAnimation; 14 import android.widget.Button; 15 import android.widget.Switch; 16 import android.widget.Toast; 17 import android.widget.ToggleButton; 18 import com.amastigote.darker.R; 19 import com.amastigote.darker.model.DarkerSettings; 20 import com.amastigote.darker.service.ScreenFilterService; 21 import com.rtugeek.android.colorseekbar.ColorSeekBar; 22 import io.feeeei.circleseekbar.CircleSeekBar; 23 24 public class MainActivity extends AppCompatActivity { 25 DarkerSettings currentDarkerSettings = new DarkerSettings(); 26 CircleSeekBar circleSeekBar_brightness; 27 CircleSeekBar circleSeekBar_alpha; 28 ColorSeekBar colorSeekBar; 29 Switch aSwitch; 30 Intent intent; 31 32 @Override 33 protected void onDestroy() { 34 if (intent != null) 35 stopService(intent); 36 super.onDestroy(); 37 } 38 39 @Override 40 protected void onCreate(Bundle savedInstanceState) { 41 super.onCreate(savedInstanceState); 42 setContentView(R.layout.activity_main); 43 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 44 toolbar.setTitle("make it darker!"); 45 setSupportActionBar(toolbar); 46 DarkerSettings.initializeContext(getApplicationContext()); 47 48 checkPermissions(); 49 50 circleSeekBar_brightness = (CircleSeekBar) findViewById(R.id.cp_brightness_circleSeekBar); 51 circleSeekBar_alpha = (CircleSeekBar) findViewById(R.id.cp_alpha_circleSeekBar); 52 colorSeekBar = (ColorSeekBar) findViewById(R.id.cp_colorSeekBar); 53 aSwitch = (Switch) findViewById(R.id.cp_useColor_switch); 54 ToggleButton toggleButton = (ToggleButton) findViewById(R.id.cm_toggle_button); 55 Button restore_settings_button = (Button) findViewById(R.id.cm_restore_settings_button); 56 57 restoreLatestSettings(); 58 59 toggleButton.setOnClickListener(new View.OnClickListener() { 60 61 @Override 62 public void onClick(View view) { 63 if (((ToggleButton) view).isChecked()) 64 collectCurrentDarkerSettings(); 65 else 66 ScreenFilterService.removeScreenFilter(); 67 } 68 }); 69 70 restore_settings_button.setOnClickListener(new View.OnClickListener() { 71 72 @Override 73 public void onClick(View view) { 74 DarkerSettings darkerSettings_default = DarkerSettings.getDefaultSettings(); 75 circleSeekBar_brightness.setCurProcess((int) (darkerSettings_default.getBrightness() * 100)); 76 circleSeekBar_alpha.setCurProcess((int) (darkerSettings_default.getAlpha() * 100)); 77 if (aSwitch.isChecked() != darkerSettings_default.isUseColor()) { 78 aSwitch.setChecked(darkerSettings_default.isUseColor()); 79 AlphaAnimation alphaAnimation_1 = new AlphaAnimation(1, 0); 80 alphaAnimation_1.setDuration(300); 81 colorSeekBar.startAnimation(alphaAnimation_1); 82 colorSeekBar.setVisibility(View.INVISIBLE); 83 } 84 } 85 }); 86 87 aSwitch.setOnClickListener(new View.OnClickListener() { 88 @Override 89 public void onClick(View view) { 90 if (((Switch) view).isChecked()) { 91 AlphaAnimation alphaAnimation_0 = new AlphaAnimation(0, 1); 92 alphaAnimation_0.setDuration(300); 93 colorSeekBar.startAnimation(alphaAnimation_0); 94 colorSeekBar.setVisibility(View.VISIBLE); 95 } 96 else { 97 AlphaAnimation alphaAnimation_1 = new AlphaAnimation(1, 0); 98 alphaAnimation_1.setDuration(300); 99 colorSeekBar.startAnimation(alphaAnimation_1); 100 colorSeekBar.setVisibility(View.INVISIBLE); 101 } 102 } 103 }); 104 105 } 106 107 @Override 108 public boolean onCreateOptionsMenu(Menu menu) { 109 // Inflate the menu; this adds items to the action bar if it is present. 110 getMenuInflater().inflate(R.menu.menu_main, menu); 111 return true; 112 } 113 114 @Override 115 public boolean onOptionsItemSelected(MenuItem item) { 116 // Handle action bar item clicks here. The action bar will 117 // automatically handle clicks on the Home/Up button, so long 118 // as you specify a parent activity in AndroidManifest.xml. 119 int id = item.getItemId(); 120 121 //noinspection SimplifiableIfStatement 122 if (id == R.id.action_settings) { 123 startActivity(new Intent(Intent.ACTION_VIEW, 124 Uri.parse("https://github.com/hwding/make-it-darker"))); 125 } 126 127 if (id == R.id.action_licenses) { 128 startActivity(new Intent(MainActivity.this, LicenseActivity.class)); 129 } 130 131 return super.onOptionsItemSelected(item); 132 } 133 134 private void collectCurrentDarkerSettings() { 135 currentDarkerSettings.setBrightness(((float) circleSeekBar_brightness.getCurProcess()) / 100); 136 currentDarkerSettings.setAlpha(((float) circleSeekBar_alpha.getCurProcess()) / 100); 137 currentDarkerSettings.setUseColor(aSwitch.isChecked()); 138 currentDarkerSettings.setColor(colorSeekBar.getColor()); 139 currentDarkerSettings.saveCurrentSettings(); 140 ScreenFilterService.updateScreenFilter(currentDarkerSettings); 141 } 142 143 private void restoreLatestSettings() { 144 DarkerSettings latestDarkerSettings = DarkerSettings.getCurrentSettings(); 145 circleSeekBar_alpha.setCurProcess((int) (latestDarkerSettings.getAlpha() * 100)); 146 circleSeekBar_brightness.setCurProcess((int) (latestDarkerSettings.getBrightness() * 100)); 147 if (latestDarkerSettings.isUseColor()) { 148 aSwitch.setChecked(true); 149 colorSeekBar.setVisibility(View.VISIBLE); 150 } 151 colorSeekBar.setColorBarValue(latestDarkerSettings.getColor()); 152 } 153 154 private void checkPermissions() { 155 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 156 if (!Settings.canDrawOverlays(getApplicationContext())) { 157 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); 158 intent.setData(Uri.parse("package:" + getPackageName())); 159 startActivityForResult(intent, 0); 160 } 161 else 162 prepareForService(); 163 } 164 else 165 prepareForService(); 166 } 167 168 @Override 169 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 170 super.onActivityResult(requestCode, resultCode, data); 171 if (requestCode == 0) { 172 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 173 if (Settings.canDrawOverlays(this)) 174 prepareForService(); 175 else { 176 Toast.makeText(getApplicationContext(), "權限請求被拒絕 無法正常工作 :(", Toast.LENGTH_LONG).show(); 177 finish(); 178 } 179 } 180 } 181 } 182 183 private void prepareForService() { 184 intent = new Intent(getApplicationContext(), ScreenFilterService.class); 185 startService(intent); 186 } 187 } MainActivity.java
高優先級的懸浮窗是敏感權限,6.0以上系統需要特殊處理。
在啟動時,首先調用checkPermissions()方法,判斷系統版本為6.0及以上時,通過包裝一個intent的方式,使app專門請求用戶授權此項權限。
返回後通過接收result,再一次判斷是否具有drawOverlays的權限,如果沒有,則報錯並正常退出,如果獲取到權限則啟動service。
而如果系統版本低於6.0,則可以直接喚起service使系統自動向用戶請求該項權限。
請見文章:
http://pcedu.pconline.com.cn/692/6928996.html
http://blog.csdn.net/yangqingqo/article/details/48371123/
http://www.cnblogs.com/mengdd/p/3824782.html
另外需要在manifest中聲明權限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
尤其要注意在startService後不能立即讀寫ScreenFilterService類中的成員變量,否則會引null異常。
所以應首先同app一起自動啟動service,並在ScreenFilterService的onCreate方法中使用removeScreenFilter()方法防止filter自動生效。
還需注意應在manifest中將mainActivity的啟動模式設為單例模式以防止在childActivity中通過navigationIcon返回主activity後導致的重啟使filter消失。
:) 暑假好開心,月底就要去上海玩了。
Android消息機制之Handler,androidhandlerAndroid為什麼要提供Handler Android建議我們不要在UI線程中執行耗時操作,因為這很
WebView的使用,WebView使用1.首先修改activity.xml中的代碼: 2.然後MainActivity中的代碼: 3.最後設置權限: <use
Android-操作欄之副標題,android-副標題 我們的目標是在操作欄右側加上一個選項菜單,點擊它就可顯示或者隱藏操作欄的副標題。 由於操作欄是在API11級
Android多媒體--MediaCodec 中文API文檔,mediacodecapi*由於工作需要,需要利用MediaCodec實現Playback及Transcod