2

I bought myself a android car unit and came at the conclusion that the minimum brightness is still to bright when driving in the night. There is a clever app called screen filter that (i think it works like this) uses a overlay over the screen to fake a screen dimming. I wanted to make this by myself because i want to enable it when it's sunset and disable it after sunrise.

After some searching in the web i found a usable code. Create a Main activity with a service called Overlayservice. The service runs when i start the app and the overlay also works fine.

The next thing i want to make is a code so i can change the overlay transparency. I want that the program starts fading when sunset starts and for example after an half hour the transparency is 50%. The problem i am facing at is i am not able to set a new colour and transparency while the overlay is active in a service.

What i tired was creating a timer in the service that sets the colour after 5 seconds but i got a crash with the error:

Only the original thread that created a view hierarchy can touch its views.

The code i now have is as following:

    package erik.autostart;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.LinearLayout;

import java.util.Timer;
import java.util.TimerTask;


public class OverlayService extends Service {

    String color;
    LinearLayout oView;
    WindowManager.LayoutParams localLayoutParams;

    private static Timer timer = new Timer();


    @Override
    public IBinder onBind(Intent i) {

        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        this.color = intent.getExtras().getString("color");
        Log.v("Message","Intent: " + Color.parseColor(this.color));

        oView = new LinearLayout(getBaseContext());
        localLayoutParams = new WindowManager.LayoutParams();

        oView.setBackgroundColor(Color.parseColor(this.color)); //De eerste 2 waardes zijn de density ff is volledig en 00 is weg de laatste 6 waardes zijn hex color waardes, kan dus zwart zijn.
        WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
        WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));


        localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
        localLayoutParams.gravity = Gravity.TOP;
        localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|

                // this is to enable the notification to recieve touch events
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |

                // Draws over status bar
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

        localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
        localLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
        localLayoutParams.format = PixelFormat.TRANSLUCENT;

        manager.addView(oView, localLayoutParams);

        timer.scheduleAtFixedRate(new Task(), 0, 500);

        return START_STICKY;
    }

    private class Task extends TimerTask
    {
        public void run()
        {
            Log.v("Message", "Tasker");
            runtask();


        }
    }

    void runtask()
    {
        if(oView!=null){
            oView.setBackgroundColor(Color.parseColor("#ff000000"));
            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
            wm.updateViewLayout(oView, oView.getLayoutParams());
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v("Message", "OverlayService started");
    }


    @Override
    public void onDestroy() {
        if(oView!=null){
            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
            wm.removeView(oView);
        }
        Log.v("Message", "Destroying OverlayService");
        super.onDestroy();
    }

}

I want to run a tasker somewhere so i can set the new colour and update the overlay. To fix this i thought perhaps stopping the service and starting it again with a new colour would help so i made a taker in the Mainactivity and stopped the service and start a new one with a different colour. The problem there is that the screen flickers.

I was hoping someone can help me with this problem. Is it even possible to do what i want?

Lithium
  • 23
  • 6
  • Hi. Have you tried using AlarmManager with PendingIntent.getService instead of TimerTask? The mechanism would be simple: you set an alarm and it indirectly calls onStartCommand of your service. You can differentiate between first start and adjustment by alarm by the way of different intents. The reason for your problem is that TimerTask runs the task in separate new thread, but to update ui, you need to issue the command from the main thread. Hope this helps and if you need a full answer, I can probably write it tomorrow, just leave a reply. It is way too late now. – WrongASP Jan 12 '17 at 23:41
  • Hi, thanks for replying. Well to he honest it would always be cool to have a little example! I thought the alarm was for intervals more then 10minutes. – Lithium Jan 13 '17 at 12:16
  • Hi. I have thought and read up about it a little and came to the conclusion that AlarmManager is a bit of an overkill, especially if you want to step through dimming but not use it to start the overlay. The solution I came up with is based on Handler class for communicating between threads. More about how to communicate between threads [here](https://developer.android.com/training/multiple-threads/communicate-ui.html) – WrongASP Jan 13 '17 at 17:37

1 Answers1

0

The complete answer I promised earlier. I'm sorry for the untested code, I haven't done anything with Android development in about half a year and don't have the setup ready to test code.

That said, probably the best way is to use Handler. The Handler is initialized to be run on the main (UI) thread, so that it can modify UI. The Dimmer executes on the UI thread, thus preventing the problem you had.

...


public class OverlayService extends Service {

    Handler handler;

    ...


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        ...

        manager.addView(oView, localLayoutParams);

        handler.postDelayed(new Dimmer("#ff000000"), 500);

        return START_STICKY;
    }

    private class Dimmer implements Runnable {
        private String color;

        public Dimmer (String color) {
            this.color = color;
        }

        public void run() {
            if(oView!=null){
                oView.setBackgroundColor(Color.parseColor(color));
                WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
                wm.updateViewLayout(oView, oView.getLayoutParams());
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.v("Message", "OverlayService started");

        handler = new Handler(Looper.getMainLooper());
    }

    ...

}
WrongASP
  • 257
  • 1
  • 7
  • Thanks for helping me out :) Unfortunately i did not have time to test it out. I will let you know if it works when i have some time to program again. – Lithium Jan 26 '17 at 21:11