package info.papdt.blackblub.services; import android.animation.Animator; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Binder; import android.os.IBinder; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import info.papdt.blackblub.C; import info.papdt.blackblub.R; import info.papdt.blackblub.receiver.TileReceiver; import info.papdt.blackblub.ui.LaunchActivity; import info.papdt.blackblub.utils.Utility; import static android.view.WindowManager.LayoutParams; public class MaskService extends Service { private WindowManager mWindowManager; private NotificationManager mNotificationManager; private AccessibilityManager mAccessibilityManager; private Notification mNoti; private View mLayout; private WindowManager.LayoutParams mLayoutParams; private int mode = C.MODE_NO_PERMISSION; private boolean isShowing = false; private static final int ANIMATE_DURATION_MILES = 250; private static final int NOTIFICATION_NO = 1024; private static int brightness = 50; private static final String TAG = MaskService.class.getSimpleName(); @Override public void onCreate() { super.onCreate(); mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mAccessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); } @Override public void onDestroy() { super.onDestroy(); destroyMaskView(); Intent broadcastIntent = new Intent(); broadcastIntent.setAction(LaunchActivity.class.getCanonicalName()); broadcastIntent.putExtra(C.EXTRA_EVENT_ID, C.EVENT_DESTORY_SERVICE); sendBroadcast(broadcastIntent); } private void createMaskView() { mAccessibilityManager.isEnabled(); updateLayoutParams(mode, -1); mLayoutParams.gravity = Gravity.CENTER; if (mLayout == null) { mLayout = new View(this); mLayout.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); mLayout.setBackgroundColor(Color.BLACK); mLayout.setAlpha(0f); } try { mWindowManager.addView(mLayout, mLayoutParams); } catch (Exception e) { e.printStackTrace(); Intent broadcastIntent = new Intent(); broadcastIntent.setAction(LaunchActivity.class.getCanonicalName()); broadcastIntent.putExtra(C.EXTRA_EVENT_ID, C.EVENT_CANNOT_START); sendBroadcast(broadcastIntent); } } private void updateLayoutParams(int mode, int paramInt) { if (mLayoutParams == null) { mLayoutParams = new LayoutParams(); } this.mAccessibilityManager.isEnabled(); switch (mode) { case C.MODE_NO_PERMISSION: mLayoutParams.type = LayoutParams.TYPE_TOAST; break; case C.MODE_NORMAL: case C.MODE_EYES_CARE: mLayoutParams.type = LayoutParams.TYPE_SYSTEM_OVERLAY; break; case C.MODE_OVERLAY_ALL: mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR; break; } if (mode == C.MODE_OVERLAY_ALL) { mLayoutParams.width = 0; mLayoutParams.height = 0; mLayoutParams.flags |= LayoutParams.FLAG_DIM_BEHIND; mLayoutParams.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; mLayoutParams.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; mLayoutParams.flags &= 0xFFDFFFFF; mLayoutParams.flags &= 0xFFFFFF7F; mLayoutParams.format = PixelFormat.OPAQUE; mLayoutParams.dimAmount = constrain((100 - paramInt) / 100.0F, 0.0F, 0.9F); } else { int max = Math.max(Utility.getTrueScreenWidth(this), Utility.getTrueScreenHeight(this)); mLayoutParams.height = mLayoutParams.width = max + 200; mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; mLayoutParams.format = PixelFormat.TRANSPARENT; float targetAlpha = (100 - brightness) * 0.01f; if (paramInt != -1) { if (isShowing) { if (Math.abs(targetAlpha - mLayout.getAlpha()) < 0.1f) { mLayout.setAlpha(targetAlpha); } else { mLayout.animate().alpha(targetAlpha).setDuration(100).start(); } } else { mLayout.animate().alpha(targetAlpha).setDuration(ANIMATE_DURATION_MILES).start(); } } } if (mLayout != null) { mLayout.setBackgroundColor(mode == C.MODE_EYES_CARE ? Color.argb(220, 200, 100, 0) : Color.BLACK); } } private float constrain(float paramFloat1, float paramFloat2, float paramFloat3) { if (paramFloat1 < paramFloat2) { return paramFloat2; } if (paramFloat1 > paramFloat3) { return paramFloat3; } return paramFloat1; } private void destroyMaskView() { isShowing = false; try { Utility.createStatusBarTiles(this, false); } catch (Exception e) { e.printStackTrace(); } cancelNotification(); if (mLayout != null) { mLayout.animate() .alpha(0f) .setDuration(ANIMATE_DURATION_MILES) .setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { try { mWindowManager.removeViewImmediate(mLayout); mLayout = null; } catch (Exception e) { } } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } } private void createNotification() { Log.i(TAG, "Create running notification"); Intent openIntent = new Intent(this, LaunchActivity.class); Intent pauseIntent = new Intent(); pauseIntent.setAction(TileReceiver.ACTION_UPDATE_STATUS); Log.i(TAG, "Create "+C.ACTION_PAUSE+" action"); pauseIntent.putExtra(C.EXTRA_ACTION, C.ACTION_PAUSE); pauseIntent.putExtra(C.EXTRA_BRIGHTNESS, brightness); Notification.Action pauseAction = new Notification.Action( R.drawable.ic_wb_incandescent_black_24dp, getString(R.string.notification_action_turn_off), PendingIntent.getBroadcast(getBaseContext(), 0, pauseIntent, Intent.FILL_IN_DATA) ); mNoti = new Notification.Builder(getApplicationContext()) .setContentTitle(getString(R.string.notification_running_title)) .setContentText(getString(R.string.notification_running_msg)) .setSmallIcon(R.drawable.ic_brightness_2_white_36dp) .addAction(pauseAction) .setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) .setAutoCancel(false) .setOngoing(true) .setOnlyAlertOnce(true) .setShowWhen(false) .build(); } // implement pause notification private void createPauseNotification(){ Log.i(TAG, "Create paused notification"); Intent openIntent = new Intent(this, LaunchActivity.class); Intent resumeIntent = new Intent(); resumeIntent.setAction(TileReceiver.ACTION_UPDATE_STATUS); resumeIntent.putExtra(C.EXTRA_ACTION, C.ACTION_START); resumeIntent.putExtra(C.EXTRA_BRIGHTNESS, brightness); Intent closeIntent = new Intent(this, MaskService.class); closeIntent.putExtra(C.EXTRA_ACTION, C.ACTION_STOP); Notification.Action resumeAction = new Notification.Action(R.drawable.ic_wb_incandescent_black_24dp, getString(R.string.notification_action_turn_on), PendingIntent.getBroadcast(getBaseContext(), 0, resumeIntent, PendingIntent.FLAG_UPDATE_CURRENT)); mNoti = new Notification.Builder(getApplicationContext()) .setContentTitle(getString(R.string.notification_paused_title)) .setContentText(getString(R.string.notification_paused_msg)) .setSmallIcon(R.drawable.ic_brightness_2_white_36dp) .addAction(resumeAction) .setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) .setAutoCancel(true) .setOngoing(false) .setOnlyAlertOnce(true) .setShowWhen(false) .setDeleteIntent(PendingIntent.getService(getBaseContext(), 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT)) .build(); } private void showPausedNotification(){ if (mNoti == null) { createPauseNotification(); } mNotificationManager.notify(NOTIFICATION_NO, mNoti); } private void cancelNotification() { try { mNotificationManager.cancel(NOTIFICATION_NO); } catch (Exception e) { e.printStackTrace(); } } @Override public int onStartCommand(Intent intent, int flags, int arg) { if (intent != null && intent.hasExtra(C.EXTRA_ACTION)) { String action = intent.getStringExtra(C.EXTRA_ACTION); brightness = intent.getIntExtra(C.EXTRA_BRIGHTNESS, 0); mode = intent.getIntExtra(C.EXTRA_MODE, mode); switch (action) { case C.ACTION_START: Log.i(TAG, "Start Mask"); if (mLayout == null){ createMaskView(); } createNotification(); startForeground(NOTIFICATION_NO, mNoti); try { updateLayoutParams(mode, brightness); mWindowManager.updateViewLayout(mLayout, mLayoutParams); Utility.createStatusBarTiles(this, true); } catch (Exception e) { // do nothing.... e.printStackTrace(); } isShowing = true; Log.i(TAG, "Set alpha:" + String.valueOf(100 - intent.getIntExtra(C.EXTRA_BRIGHTNESS, 0))); break; case C.ACTION_PAUSE: Log.i(TAG, "Pause Mask"); stopForeground(true); destroyMaskView(); createPauseNotification(); showPausedNotification(); isShowing = false; break; case C.ACTION_STOP: Log.i(TAG, "Stop Mask"); isShowing = false; stopSelf(); break; case C.ACTION_UPDATE: mAccessibilityManager.isEnabled(); Log.i(TAG, "Update Mask"); isShowing = true; try { updateLayoutParams(mode, brightness); mWindowManager.updateViewLayout(mLayout, mLayoutParams); } catch (Exception e) { // do nothing.... } Log.i(TAG, "Set alpha:" + String.valueOf(100 - intent.getIntExtra(C.EXTRA_BRIGHTNESS, 0))); break; } } if (intent != null && !intent.getBooleanExtra(C.EXTRA_DO_NOT_SEND_CHECK, false)) { Log.i(TAG, "Check Mask. Check from toggle:" + intent.getBooleanExtra(C.EXTRA_CHECK_FROM_TOGGLE, false)); Intent broadcastIntent = new Intent(); broadcastIntent.setAction( intent.getBooleanExtra(C.EXTRA_CHECK_FROM_TOGGLE, false) ? "info.papdt.blackbulb.ACTION_TOGGLE" : "info.papdt.blackbulb.ACTION_UPDATE_ACTIVITY_TOGGLE" ); broadcastIntent.putExtra(C.EXTRA_EVENT_ID, C.EVENT_CHECK); broadcastIntent.putExtra("isShowing", isShowing); sendBroadcast(broadcastIntent); } return START_STICKY; } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } private MaskBinder mBinder = new MaskBinder(); public class MaskBinder extends Binder { public boolean isMaskShowing() { return isShowing; } } }