Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25b3b8e5 authored by John Spurlock's avatar John Spurlock Committed by Android (Google) Code Review
Browse files

Merge "Create a basic doze dream in SystemUI."

parents dcad4f8a bf370992
Loading
Loading
Loading
Loading
+64 −40
Original line number Diff line number Diff line
@@ -161,6 +161,7 @@ public class DreamService extends Service implements Window.Callback {
    private boolean mFinished;
    private boolean mCanDoze;
    private boolean mDozing;
    private boolean mWindowless;
    private DozeHardware mDozeHardware;

    private boolean mDebug = false;
@@ -519,6 +520,24 @@ public class DreamService extends Service implements Window.Callback {
        return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright);
    }

    /**
     * Marks this dream as windowless.  Only available to doze dreams.
     *
     * @hide
     */
    public void setWindowless(boolean windowless) {
        mWindowless = windowless;
    }

    /**
     * Returns whether or not this dream is windowless.  Only available to doze dreams.
     *
     * @hide
     */
    public boolean isWindowless() {
        return mWindowless;
    }

    /**
     * Returns true if this dream is allowed to doze.
     * <p>
@@ -715,6 +734,7 @@ public class DreamService extends Service implements Window.Callback {
            WindowManagerGlobal.getInstance().closeAll(mWindowToken,
                    this.getClass().getName(), "Dream");
            mWindowToken = null;
            mCanDoze = false;
        }
    }

@@ -744,7 +764,10 @@ public class DreamService extends Service implements Window.Callback {

        mWindowToken = windowToken;
        mCanDoze = canDoze;

        if (mWindowless && !mCanDoze) {
            throw new IllegalStateException("Only doze dreams can be windowless");
        }
        if (!mWindowless) {
            mWindow = PolicyManager.makeNewWindow(this);
            mWindow.setCallback(this);
            mWindow.requestFeature(Window.FEATURE_NO_TITLE);
@@ -784,7 +807,7 @@ public class DreamService extends Service implements Window.Callback {
                mWindow = null;
                return;
            }

        }
        // We need to defer calling onDreamingStarted until after onWindowAttached,
        // which is posted to the handler by addView, so we post onDreamingStarted
        // to the handler also.  Need to watch out here in case detach occurs before
@@ -792,7 +815,7 @@ public class DreamService extends Service implements Window.Callback {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mWindow != null) {
                if (mWindow != null || mWindowless) {
                    if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
                    mStarted = true;
                    onDreamingStarted();
@@ -878,6 +901,7 @@ public class DreamService extends Service implements Window.Callback {
                if (isLowProfile()) pw.print(" lowprofile");
                if (isFullscreen()) pw.print(" fullscreen");
                if (isScreenBright()) pw.print(" bright");
                if (isWindowless()) pw.print(" windowless");
                if (isDozing()) pw.print(" dozing");
                pw.println();
            }
+2 −3
Original line number Diff line number Diff line
@@ -1184,7 +1184,7 @@
    <bool name="config_dreamsActivatedOnDockByDefault">true</bool>
    <!-- If supported and enabled, are dreams activated when asleep and charging? (by default) -->
    <bool name="config_dreamsActivatedOnSleepByDefault">false</bool>
    <!-- ComponentName of the default dream (Settings.Secure.SCREENSAVER_COMPONENT) -->
    <!-- ComponentName of the default dream (Settings.Secure.DEFAULT_SCREENSAVER_COMPONENT) -->
    <string name="config_dreamsDefaultComponent">com.google.android.deskclock/com.android.deskclock.Screensaver</string>

    <!-- Are we allowed to dream while not plugged in? -->
@@ -1205,8 +1205,7 @@
         try to start this dream if possible.  The dream should typically call startDozing()
         to put the display into a low power state and allow the application processor
         to be suspended.  When the dream ends, the system will go to sleep as usual.
         Specify the component name (Settings.Secure.SCREENSAVER_COMPONENT) or an
         empty string if none.
         Specify the component name or an empty string if none.

         Note that doze dreams are not subject to the same start conditions as ordinary dreams.
         Doze dreams will run whenever the power manager is in a dozing state. -->
+6 −0
Original line number Diff line number Diff line
@@ -300,5 +300,11 @@
                <category android:name="android.intent.category.DESK_DOCK" />
            </intent-filter>
        </activity>


        <!-- I dream of notifications -->
        <service
            android:name=".doze.DozeService"
            android:exported="true" />
    </application>
</manifest>
+238 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.systemui.doze;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.os.PowerManager;
import android.os.Vibrator;
import android.service.dreams.DozeHardware;
import android.service.dreams.DreamService;
import android.util.Log;

import com.android.systemui.SystemUIApplication;

public class DozeService extends DreamService {
    private static final boolean DEBUG = false;

    private static final String TEASE_ACTION = "com.android.systemui.doze.tease";

    private final String mTag = String.format("DozeService.%08x", hashCode());
    private final Context mContext = this;

    private Host mHost;
    private DozeHardware mDozeHardware;
    private SensorManager mSensors;
    private Sensor mSigMotionSensor;
    private PowerManager mPowerManager;
    private PowerManager.WakeLock mWakeLock;
    private boolean mDreaming;
    private boolean mTeaseReceiverRegistered;

    public DozeService() {
        if (DEBUG) Log.d(mTag, "new DozeService()");
        setDebug(DEBUG);
    }

    @Override
    public void onCreate() {
        if (DEBUG) Log.d(mTag, "onCreate");
        super.onCreate();

        if (getApplication() instanceof SystemUIApplication) {
            final SystemUIApplication app = (SystemUIApplication) getApplication();
            mHost = app.getComponent(Host.class);
        }

        setWindowless(true);

        mSensors = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
        mSigMotionSensor = mSensors.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
    }

    @Override
    public void onAttachedToWindow() {
        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
        super.onAttachedToWindow();
    }

    @Override
    public void onDreamingStarted() {
        super.onDreamingStarted();
        mDozeHardware = getDozeHardware();
        if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze()
                + " dozeHardware=" + mDozeHardware);
        mDreaming = true;
        listenForTeaseSignals(true);
        requestDoze();
    }

    public void stayAwake(long millis) {
        if (mDreaming && millis > 0) {
            mWakeLock.acquire(millis);
        }
    }

    public void startDozing() {
        if (DEBUG) Log.d(mTag, "startDozing mDreaming=" + mDreaming);
        if (!mDreaming) {
            Log.w(mTag, "Not dozing, no longer dreaming");
            return;
        }

        super.startDozing();
    }

    @Override
    public void onDreamingStopped() {
        if (DEBUG) Log.d(mTag, "onDreamingStopped isDozing=" + isDozing());
        super.onDreamingStopped();

        mDreaming = false;
        mDozeHardware = null;
        if (mWakeLock.isHeld()) {
            mWakeLock.release();
        }
        listenForTeaseSignals(false);
        stopDozing();
        dozingStopped();
    }

    @Override
    public void onDetachedFromWindow() {
        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
        super.onDetachedFromWindow();

        dozingStopped();
    }

    @Override
    public void onDestroy() {
        if (DEBUG) Log.d(mTag, "onDestroy");
        super.onDestroy();

        dozingStopped();
    }

    private void requestDoze() {
        if (mHost != null) {
            mHost.requestDoze(this);
        }
    }

    private void requestTease() {
        if (mHost != null) {
            mHost.requestTease(this);
        }
    }

    private void dozingStopped() {
        if (mHost != null) {
            mHost.dozingStopped(this);
        }
    }

    private void listenForTeaseSignals(boolean listen) {
        if (DEBUG) Log.d(mTag, "listenForTeaseSignals: " + listen);
        if (mHost == null) return;
        listenForSignificantMotion(listen);
        if (listen) {
            mContext.registerReceiver(mTeaseReceiver, new IntentFilter(TEASE_ACTION));
            mTeaseReceiverRegistered = true;
            mHost.addCallback(mHostCallback);
        } else {
            if (mTeaseReceiverRegistered) {
                mContext.unregisterReceiver(mTeaseReceiver);
            }
            mTeaseReceiverRegistered = false;
            mHost.removeCallback(mHostCallback);
        }
    }

    private void listenForSignificantMotion(boolean listen) {
        if (mSigMotionSensor == null) return;
        if (listen) {
            mSensors.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
        } else {
            mSensors.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
        }
    }

    private static String triggerEventToString(TriggerEvent event) {
        if (event == null) return null;
        final StringBuilder sb = new StringBuilder("TriggerEvent[")
                .append(event.timestamp).append(',')
                .append(event.sensor.getName());
        if (event.values != null) {
            for (int i = 0; i < event.values.length; i++) {
                sb.append(',').append(event.values[i]);
            }
        }
        return sb.append(']').toString();
    }

    private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
        @Override
        public void onTrigger(TriggerEvent event) {
            if (DEBUG) Log.d(mTag, "sigMotion.onTrigger: " + triggerEventToString(event));
            if (DEBUG) {
                final Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
                if (v != null) {
                    v.vibrate(1000);
                }
            }
            requestTease();
            listenForSignificantMotion(true);  // reregister, this sensor only fires once
        }
    };

    private final BroadcastReceiver mTeaseReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Log.d(mTag, "Received tease intent");
            requestTease();
        }
    };

    private final Host.Callback mHostCallback = new Host.Callback() {
        @Override
        public void onNewNotifications() {
            if (DEBUG) Log.d(mTag, "onNewNotifications");
            requestTease();
        }
    };

    public interface Host {
        void addCallback(Callback callback);
        void removeCallback(Callback callback);
        void requestDoze(DozeService dozeService);
        void requestTease(DozeService dozeService);
        void dozingStopped(DozeService dozeService);

        public interface Callback {
            void onNewNotifications();
        }
    }
}
+32 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -89,6 +91,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
            = new PathInterpolator(0, 0, 0.5f, 1);

    private boolean mDimmed;
    private boolean mDark;
    private final Paint mDarkPaint = createDarkPaint();

    private int mBgResId = com.android.internal.R.drawable.notification_material_bg;
    private int mDimmedBgResId = com.android.internal.R.drawable.notification_material_bg_dim;
@@ -295,6 +299,34 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
        }
    }

    public void setDark(boolean dark, boolean fade) {
        // TODO implement fade
        if (mDark != dark) {
            mDark = dark;
            if (mDark) {
                setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
            } else {
                setLayerType(View.LAYER_TYPE_NONE, null);
            }
        }
    }

    private static Paint createDarkPaint() {
        final Paint p = new Paint();
        final float[] invert = {
            -1f,  0f,  0f, 1f, 1f,
             0f, -1f,  0f, 1f, 1f,
             0f,  0f, -1f, 1f, 1f,
             0f,  0f,  0f, 1f, 0f
        };
        final ColorMatrix m = new ColorMatrix(invert);
        final ColorMatrix grayscale = new ColorMatrix();
        grayscale.setSaturation(0);
        m.preConcat(grayscale);
        p.setColorFilter(new ColorMatrixColorFilter(m));
        return p;
    }

    /**
     * Sets the resource id for the background of this notification.
     *
Loading