Делаем симпатичный виджет регулировки яркости

Давным давно у моей мамы на телефоне было приложение, которое позволяло изменить яркость экрана проведя пальцем по его левому краю. Мама к нему привыкла, а потом перешла на новый телефон и там уже то приложение из магазина исчезло. Собственно, не найдя аналогов, я решил написать его сам и подарить ей на день рождения. Пошаговое руководство по созданию и результат — в этой статье.

Для создания этого приложения нам потребуется два разрешения — разрешение на изменение системных настроек (непосредственно регулировка яркости) и разрешение наложения поверх других приложений. Оба этих разрешения начиная с версии Android 6.0 нельзя получить просто указав их в манифесте — их необходимо запросить у пользователя по отдельности.

Структура нашего приложения будет следующая — основная активность, которая будет иметь две кнопки — одна будет запускать службу, которая будет отрисовывать SeekBar для управления яркостью, а вторая будет эту службу останавливать. Начнем с нашей основной активности. Вот как выглядит файл разметки:

Разметка mainActivity

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:orientation="vertical"     >      <Button         android:id="@+id/startService"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_alignParentTop="true"         android:layout_centerHorizontal="true"         android:layout_marginTop="27dp"         android:text="Показать Виджет" />      <Button         android:id="@+id/stopService"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_alignLeft="@+id/startService"         android:layout_below="@+id/startService"         android:layout_marginTop="39dp"         android:text="Спрятать виджет" />   </LinearLayout> 

И вот как будет выглядеть сама активность:

mainActivity

import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button;  public class MainActivity extends AppCompatActivity {       @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);                        //Здесь мы проверяем, какая версия Android у пользователя, и если она 6 или выше - запрашиваем разрешение сложным способом         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES .M) {             if (!Settings.System.canWrite(getApplicationContext())) {                 Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, Uri.parse("package:" + getPackageName()));                 Intent myIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);                 startActivity(myIntent);                 startActivity(intent);             }             else {             }         }                  final Button startService = (Button) findViewById(R.id.startService);         final Button stopService = (Button) findViewById(R.id.stopService);          startService.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 startService(new Intent(getApplication(), BrightnessChekingService.class));             }         });          stopService.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 stopService(new Intent(getApplication(), BrightnessChekingService.class));             }         });      }  } 

Теперь создадим службу BrightnessChekingService, ее функции я уже указал выше. Не забывайте — в случае, если вы создаете ее вручную, необходимо зарегистрировать ее в манифесте

BrightnessChekingService

import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.os.Build; import android.os.IBinder; import android.provider.Settings; import android.view.Gravity; import android.view.WindowManager; import android.widget.SeekBar;   public class BrightnessChekingService extends Service{     private WindowManager windowManager;     private WindowManager.LayoutParams params;     private VerticalSeekBar seekBar;     private Context context;      public void onCreate () {         super.onCreate();                  //Тут нам тоже необходимо понять, какая у пользователя версия Android, так как названия этого разрешения в разных версиях разное         int LAYOUT_FLAG;         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {             LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;         } else {             LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;         }          windowManager =  (WindowManager) getSystemService(WINDOW_SERVICE);         context = this.getApplicationContext();         seekBar = new VerticalSeekBar(context);         seekBar.setMax(255);         float alpha = (float) 0.1;         seekBar.setAlpha(alpha);          params = new WindowManager.LayoutParams(                 WindowManager.LayoutParams.WRAP_CONTENT,                 WindowManager.LayoutParams.MATCH_PARENT,                 LAYOUT_FLAG,                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,                 PixelFormat.TRANSLUCENT);          params.gravity = Gravity.START;         params.x = 0;         params.y = 0;         params.height = 700;           windowManager.addView(seekBar, params);          int Brightnes = Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);          seekBar.setProgress(Brightnes);         seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){              public void onProgressChanged(SeekBar seekBar, int i, boolean b) {                  Settings.System.putInt(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,i);                 //Следующие строчки нужны для того, чтобы отключить автояркость и позволить seekBar ее регулировать                 Settings.System.putInt(getContentResolver(),                         Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);              }              public void onStartTrackingTouch(SeekBar seekBar) {              }              public void onStopTrackingTouch (SeekBar seekBar) {              }          });      }      public void onDestroy() {         super.onDestroy();         if (seekBar != null)             windowManager.removeView(seekBar);     }      @Override     public IBinder onBind(Intent intent) {         return null;     } } 

Ну и напоследок, создадим класс VerticalSeekBar который будет являться кастомной версией обычного SeekBar, отличие будет в том, что наш SeekBar будет располагаться вертикально

VerticalSeekBar

import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.SeekBar;    public class VerticalSeekBar extends SeekBar {     public VerticalSeekBar (Context context) {         super(context);      }      public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {         super(context, attrs, defStyle);      }      public VerticalSeekBar(Context context, AttributeSet attrs) {         super(context, attrs);      }      protected void onSizeChanged(int w, int h, int oldw, int oldh) {         super.onSizeChanged(h, w, oldh, oldw);     }      public synchronized void setProgress(int progress)       {         super.setProgress(progress);         onSizeChanged(getWidth(), getHeight(), 0, 0);     }      protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {         super.onMeasure(heightMeasureSpec, widthMeasureSpec);         setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());     }      protected void onDraw(Canvas c) {         c.rotate(-90);         c.translate(-getHeight(), 0);          super.onDraw(c);     }      @Override     public boolean onTouchEvent(MotionEvent event) {          if (!isEnabled()) {             return false;         }          switch (event.getAction()) {             case MotionEvent.ACTION_DOWN:             case MotionEvent.ACTION_MOVE:             case MotionEvent.ACTION_UP:                 setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));                 onSizeChanged(getWidth(), getHeight(), 0, 0);                 break;              case MotionEvent.ACTION_CANCEL:                 break;         }          return true;      } } 

Вот как при этом будет выглядеть манифест приложения:

Mанифест

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="com.example.bright">      <uses-permission android:name="android.permission.WRITE_SETTINGS" />     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />      <application         android:allowBackup="true"         android:icon="@mipmap/ic_launcher"         android:label="@string/app_name"         android:roundIcon="@mipmap/ic_launcher_round"         android:supportsRtl="true"         android:theme="@style/AppTheme">         <activity android:name=".MainActivity">             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                  <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>         <service             android:name=".BrightnessChekingService"             android:enabled="true"             android:exported="true"></service>     </application>  </manifest> 

Вот и все, простенький, но сделанный с душой подарок маме на день рождения готов. Получилась у нас вот такая штука:


Для демонстрации я убрал строчку, задающую прозрачность, так как в этом режиме полоска практически незаметна. При желании можно добавить кнопки, регулирующие размер полоски, ее положение на экране и прозрачность.

Вот тут можно взять апкшник. Если кто-то будет пользоваться хотелось бы получить фидбек, так как тестировал только на седьмом андроиде. И хочу выразить огромную благодарность тем, кто уже тестирует приложение и докладывается о багах — вы невероятно мне помогаете. Спасибо!

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий