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

Commit 26f8bf25 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Add animation for power saver dialog" into tm-dev

parents 5e86cee0 4293d98e
Loading
Loading
Loading
Loading
+25 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.power;

import static android.app.PendingIntent.FLAG_IMMUTABLE;

import android.app.Dialog;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -60,20 +61,25 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.volume.Events;

import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Objects;

import javax.inject.Inject;

import dagger.Lazy;

/**
 */
@SysUISingleton
@@ -164,11 +170,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
    private ActivityStarter mActivityStarter;
    private final BroadcastSender mBroadcastSender;

    private final Lazy<BatteryController> mBatteryControllerLazy;
    private final DialogLaunchAnimator mDialogLaunchAnimator;

    /**
     */
    @Inject
    public PowerNotificationWarnings(Context context, ActivityStarter activityStarter,
            BroadcastSender broadcastSender) {
            BroadcastSender broadcastSender, Lazy<BatteryController> batteryControllerLazy,
            DialogLaunchAnimator dialogLaunchAnimator) {
        mContext = context;
        mNoMan = mContext.getSystemService(NotificationManager.class);
        mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -176,6 +186,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
        mReceiver.init();
        mActivityStarter = activityStarter;
        mBroadcastSender = broadcastSender;
        mBatteryControllerLazy = batteryControllerLazy;
        mDialogLaunchAnimator = dialogLaunchAnimator;
        mUseSevereDialog = mContext.getResources().getBoolean(R.bool.config_severe_battery_dialog);
    }

@@ -685,8 +697,19 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
        }
        d.setShowForAllUsers(true);
        d.setOnDismissListener((dialog) -> mSaverConfirmation = null);
        WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView();
        if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) {
            mDialogLaunchAnimator.showFromView(d, ref.get());
        } else {
            d.show();
        }
        mSaverConfirmation = d;
        mBatteryControllerLazy.get().clearLastPowerSaverStartView();
    }

    @VisibleForTesting
    Dialog getSaverConfirmationDialog() {
        return mSaverConfirmation;
    }

    private boolean isEnglishLocale() {
+6 −1
Original line number Diff line number Diff line
@@ -116,6 +116,11 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
    public void handleSetListening(boolean listening) {
        super.handleSetListening(listening);
        mSetting.setListening(listening);
        if (!listening) {
            // If we stopped listening, it means that the tile is not visible. In that case, we
            // don't need to save the view anymore
            mBatteryController.clearLastPowerSaverStartView();
        }
    }

    @Override
@@ -128,7 +133,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
        if (getState().state == Tile.STATE_UNAVAILABLE) {
            return;
        }
        mBatteryController.setPowerSaveMode(!mPowerSave);
        mBatteryController.setPowerSaveMode(!mPowerSave, view);
    }

    @Override
+28 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;

import android.annotation.Nullable;
import android.view.View;

import com.android.systemui.Dumpable;
import com.android.systemui.demomode.DemoMode;
@@ -24,6 +25,7 @@ import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChang

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;

public interface BatteryController extends DemoMode, Dumpable,
        CallbackController<BatteryStateChangeCallback> {
@@ -35,7 +37,32 @@ public interface BatteryController extends DemoMode, Dumpable,
    /**
     * Sets if the current device is in power save mode.
     */
    void setPowerSaveMode(boolean powerSave);
    default void setPowerSaveMode(boolean powerSave) {
        setPowerSaveMode(powerSave, null);
    }

    /**
     * Sets if the current device is in power save mode.
     *
     * Can pass the view that triggered the request.
     */
    void setPowerSaveMode(boolean powerSave, @Nullable View view);

    /**
     * Gets a reference to the last view used when called {@link #setPowerSaveMode}.
     */
    @Nullable
    default WeakReference<View> getLastPowerSaverStartView() {
        return null;
    }

    /**
     * Clears the last view used when called {@link #setPowerSaveMode}.
     *
     * Immediately after calling this, a call to {@link #getLastPowerSaverStartView()} should return
     * {@code null}.
     */
    default void clearLastPowerSaverStartView() {}

    /**
     * Returns {@code true} if the device is currently plugged in.
+20 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,8 +46,10 @@ import com.android.systemui.power.EnhancedEstimates;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Default implementation of a {@link BatteryController}. This controller monitors for battery
@@ -85,6 +88,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
    private Estimate mEstimate;
    private boolean mFetchingEstimate = false;

    // Use AtomicReference because we may request it from a different thread
    // Use WeakReference because we are keeping a reference to a View that's not as long lived
    // as this controller.
    private AtomicReference<WeakReference<View>> mPowerSaverStartView = new AtomicReference<>();

    @VisibleForTesting
    public BatteryControllerImpl(
            Context context,
@@ -141,10 +149,21 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
    }

    @Override
    public void setPowerSaveMode(boolean powerSave) {
    public void setPowerSaveMode(boolean powerSave, View view) {
        if (powerSave) mPowerSaverStartView.set(new WeakReference<>(view));
        BatterySaverUtils.setPowerSaveMode(mContext, powerSave, /*needFirstTimeWarning*/ true);
    }

    @Override
    public WeakReference<View> getLastPowerSaverStartView() {
        return mPowerSaverStartView.get();
    }

    @Override
    public void clearLastPowerSaverStartView() {
        mPowerSaverStartView.set(null);
    }

    @Override
    public void addCallback(@NonNull BatteryController.BatteryStateChangeCallback cb) {
        synchronized (mChangeCallbacks) {
+92 −5
Original line number Diff line number Diff line
@@ -25,29 +25,48 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;

import androidx.test.runner.AndroidJUnit4;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;

import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.lang.ref.WeakReference;

@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PowerNotificationWarningsTest extends SysuiTestCase {

    public static final String FORMATTED_45M = "0h 45m";
@@ -55,14 +74,34 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
    private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
    private PowerNotificationWarnings mPowerNotificationWarnings;

    @Mock
    private BatteryController mBatteryController;
    @Mock
    private DialogLaunchAnimator mDialogLaunchAnimator;
    @Mock
    private View mView;

    private BroadcastReceiver mReceiver;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        Context wrapper = new ContextWrapper(mContext) {
            @Override
            public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
                    IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
                mReceiver = receiver;
                return null;
            }
        };

        // Test Instance.
        mContext.addMockSystemService(NotificationManager.class, mMockNotificationManager);
        ActivityStarter starter = mDependency.injectMockDependency(ActivityStarter.class);
        BroadcastSender broadcastSender = mDependency.injectMockDependency(BroadcastSender.class);
        mPowerNotificationWarnings = new PowerNotificationWarnings(mContext, starter,
                broadcastSender);
        mPowerNotificationWarnings = new PowerNotificationWarnings(wrapper, starter,
                broadcastSender, () -> mBatteryController, mDialogLaunchAnimator);
        BatteryStateSnapshot snapshot = new BatteryStateSnapshot(100, false, false, 1,
                BatteryManager.BATTERY_HEALTH_GOOD, 5, 15);
        mPowerNotificationWarnings.updateSnapshot(snapshot);
@@ -168,4 +207,52 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {

        mPowerNotificationWarnings.mUsbHighTempDialog.dismiss();
    }

    @Test
    public void testDialogStartedFromLauncher_viewVisible() {
        when(mBatteryController.getLastPowerSaverStartView())
                .thenReturn(new WeakReference<>(mView));
        when(mView.isAggregatedVisible()).thenReturn(true);

        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
        intent.putExtras(new Bundle());

        mReceiver.onReceive(mContext, intent);

        verify(mDialogLaunchAnimator).showFromView(any(), eq(mView));

        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
    }

    @Test
    public void testDialogStartedNotFromLauncher_viewNotVisible() {
        when(mBatteryController.getLastPowerSaverStartView())
                .thenReturn(new WeakReference<>(mView));
        when(mView.isAggregatedVisible()).thenReturn(false);

        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
        intent.putExtras(new Bundle());

        mReceiver.onReceive(mContext, intent);

        verify(mDialogLaunchAnimator, never()).showFromView(any(), any());

        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
    }

    @Test
    public void testDialogShownNotFromLauncher() {
        when(mBatteryController.getLastPowerSaverStartView()).thenReturn(null);

        Intent intent = new Intent(BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION);
        intent.putExtras(new Bundle());

        mReceiver.onReceive(mContext, intent);

        verify(mDialogLaunchAnimator, never()).showFromView(any(), any());

        assertThat(mPowerNotificationWarnings.getSaverConfirmationDialog().isShowing()).isTrue();
        mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
    }
}
Loading