Making a cute brightness adjustment widget

  • Tutorial
A long time ago, my mom had an application on the phone that allowed you to change the brightness of the screen by swiping your finger along its left edge. Mom got used to it, and then moved to a new phone and there already that application from the store disappeared. Actually, having not found analogues, I decided to write it myself and give it to her for my birthday. A step-by-step guide to creating and the result is in this article.

To create this application, we need two permissions - permission to change the system settings (directly adjust the brightness) and the resolution of the overlay on top of other applications. Both of these permissions starting with the version of Android 6.0 can not be obtained simply by specifying them in the manifest - they must be requested from the user separately.

The structure of our application will be the following - the main activity, which will have two buttons - one will start the service that will draw the SeekBar for brightness control, and the second will stop this service. Let's start with our main activity. Here is the markup file:
MainActivity Markup
<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns: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"
    ><Buttonandroid: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="Показать Виджет" /><Buttonandroid: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>


And this is how the activity itself will look like:
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;
publicclassMainActivityextendsAppCompatActivity{
    @OverrideprotectedvoidonCreate(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() {
            @OverridepublicvoidonClick(View v){
                startService(new Intent(getApplication(), BrightnessChekingService.class));
            }
        });
        stopService.setOnClickListener(new View.OnClickListener() {
            @OverridepublicvoidonClick(View v){
                stopService(new Intent(getApplication(), BrightnessChekingService.class));
            }
        });
    }
}


Now we will create the BrightnessChekingService service, I have already indicated its functions above. Do not forget - in case you create it manually, you must register it in the manifest
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;
publicclassBrightnessChekingServiceextendsService{
    private WindowManager windowManager;
    private WindowManager.LayoutParams params;
    private VerticalSeekBar seekBar;
    private Context context;
    publicvoidonCreate(){
        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(){
            publicvoidonProgressChanged(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);
            }
            publicvoidonStartTrackingTouch(SeekBar seekBar){
            }
            publicvoidonStopTrackingTouch(SeekBar seekBar){
            }
        });
    }
    publicvoidonDestroy(){
        super.onDestroy();
        if (seekBar != null)
            windowManager.removeView(seekBar);
    }
    @Overridepublic IBinder onBind(Intent intent){
        returnnull;
    }
}


And finally, let's create a VerticalSeekBar class which will be a custom version of the regular SeekBar, the difference will be that our SeekBar will be located vertically
VerticalSeekBar
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;
publicclassVerticalSeekBarextendsSeekBar{
    publicVerticalSeekBar(Context context){
        super(context);
    }
    publicVerticalSeekBar(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
    }
    publicVerticalSeekBar(Context context, AttributeSet attrs){
        super(context, attrs);
    }
    protectedvoidonSizeChanged(int w, int h, int oldw, int oldh){
        super.onSizeChanged(h, w, oldh, oldw);
    }
    publicsynchronizedvoidsetProgress(int progress){
        super.setProgress(progress);
        onSizeChanged(getWidth(), getHeight(), 0, 0);
    }
    protectedsynchronizedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec){
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }
    protectedvoidonDraw(Canvas c){
        c.rotate(-90);
        c.translate(-getHeight(), 0);
        super.onDraw(c);
    }
    @OverridepublicbooleanonTouchEvent(MotionEvent event){
        if (!isEnabled()) {
            returnfalse;
        }
        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;
        }
        returntrue;
    }
}


Here is how the application manifest will look like:
Manifesto
<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"package="com.example.bright"><uses-permissionandroid:name="android.permission.WRITE_SETTINGS" /><uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW" /><applicationandroid: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"><activityandroid:name=".MainActivity"><intent-filter><actionandroid:name="android.intent.action.MAIN" /><categoryandroid:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name=".BrightnessChekingService"android:enabled="true"android:exported="true"></service></application></manifest>


That's all, a simple, but heartfelt gift for mom for a birthday is ready. It turned out we have such a thing:


For the demonstration, I removed the line that sets the transparency, since in this mode the strip is almost imperceptible. If desired, you can add buttons that adjust the size of the strip, its position on the screen and transparency.

Here it is possible to take. If someone would use I would like to get feedback, as I tested only on the seventh android. And I want to express my deep gratitude to those who are already testing the application and reporting bugs - you are incredibly helping me. Thank!

Also popular now: