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

Commit edfbfe92 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Automerger Merge Worker
Browse files

Merge "Add animation for power saver dialog" into tm-dev am: 26f8bf25 am: 13979a11

parents 7faf546f 13979a11
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