diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d4357318c8a..9a771ac9848 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1892,7 +1892,8 @@ android:name=".development.DevelopmentSettingsDisabledActivity" android:icon="@drawable/ic_settings_development" android:label="@string/development_settings_title" - android:theme="@android:style/Theme.NoDisplay"> + android:excludeFromRecents="true" + android:theme="@style/Transparent"> @@ -3069,10 +3070,12 @@ - #fdd835 #f44336 + + #ff00bcd4 + #ffef6c00 + #ff4caf50 + #fffdd835 + #ff9e9e9e diff --git a/res/xml/power_usage_detail.xml b/res/xml/power_usage_detail.xml index 0493a51799c..1891702b6eb 100644 --- a/res/xml/power_usage_detail.xml +++ b/res/xml/power_usage_detail.xml @@ -15,7 +15,9 @@ limitations under the License. --> - + - + android:selectable="true" + settings:userRestriction="no_control_apps"/> colors) { + mBounds = bounds; + mBorderWidth = borderWidth; + mEvaluator = new ArgbEvaluator(); + mErrorColor = context.getResources() + .getColor(R.color.face_anim_particle_error, context.getTheme()); + mIndex = index; + mListener = listener; + + mCurrentAngle = (float) index / totalParticles * 2 * (float) Math.PI; + mOffsetTimeSec = (float) index / totalParticles + * (1 / ROTATION_SPEED_NORMAL) * 2 * (float) Math.PI; + + mPaint = new Paint(); + mAssignedColor = colors.get(index % colors.size()); + mPaint.setColor(mAssignedColor); + mPaint.setAntiAlias(true); + mPaint.setStrokeWidth(mCurrentSize); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setStrokeCap(Paint.Cap.ROUND); + } + + public void updateState(int animationState) { + if (mAnimationState == animationState) { + Log.w(TAG, "Already in state " + animationState); + return; + } + if (animationState == ParticleCollection.STATE_COMPLETE) { + mPaint.setStyle(Paint.Style.STROKE); + } + mLastAnimationState = mAnimationState; + mAnimationState = animationState; + } + + // There are two types of particles, secondary and primary. Primary particles accelerate faster + // during the "completed" animation. Particles are secondary by default. + public void setAsPrimary() { + mSweepRate = RING_SWEEP_GROW_RATE_PRIMARY; + } + + public void update(long t, long dt) { + if (mAnimationState != ParticleCollection.STATE_COMPLETE) { + updateDot(t, dt); + } else { + updateRing(t, dt); + } + } + + private void updateDot(long t, long dt) { + final float dtSec = 0.001f * dt; + final float tSec = 0.001f * t; + + final float multiplier = mRotationSpeed / ROTATION_SPEED_NORMAL; + + // Calculate rotation speed / angle + if ((mAnimationState == ParticleCollection.STATE_STOPPED_COLORFUL + || mAnimationState == ParticleCollection.STATE_STOPPED_GRAY) + && mRotationSpeed > 0) { + // Linear slow down for now + mRotationSpeed = Math.max(mRotationSpeed - ROTATION_ACCELERATION_SPEED * dtSec, 0); + } else if (mAnimationState == ParticleCollection.STATE_STARTED + && mRotationSpeed < ROTATION_SPEED_NORMAL) { + // Linear speed up for now + mRotationSpeed += ROTATION_ACCELERATION_SPEED * dtSec; + } + + mCurrentAngle += dtSec * mRotationSpeed; + + // Calculate dot / ring size; linearly proportional with rotation speed + mCurrentSize = + (MAX_STROKE_WIDTH - MIN_STROKE_WIDTH) / 2 + * (float) Math.sin(tSec * PULSE_SPEED_NORMAL + mOffsetTimeSec) + + (MAX_STROKE_WIDTH + MIN_STROKE_WIDTH) / 2; + mCurrentSize = (mCurrentSize - MIN_STROKE_WIDTH) * multiplier + MIN_STROKE_WIDTH; + + // Calculate paint color; linearly proportional to rotation speed + int color = mAssignedColor; + if (mAnimationState == ParticleCollection.STATE_STOPPED_GRAY) { + color = (int) mEvaluator.evaluate(1 - multiplier, mAssignedColor, mErrorColor); + } else if (mLastAnimationState == ParticleCollection.STATE_STOPPED_GRAY) { + color = (int) mEvaluator.evaluate(1 - multiplier, mAssignedColor, mErrorColor); + } + + mPaint.setColor(color); + mPaint.setStrokeWidth(mCurrentSize); + } + + private void updateRing(long t, long dt) { + final float dtSec = 0.001f * dt; + final float tSec = 0.001f * t; + + // Store the start time, since we need to guarantee all rings reach final size at same time + // independent of current size. The magic 0 check is safe. + if (mRingAdjustRate == 0) { + mRingAdjustRate = + (FINAL_RING_STROKE_WIDTH - mCurrentSize) / RING_SIZE_FINALIZATION_TIME; + if (mRingCompletionTime == 0) { + mRingCompletionTime = tSec + RING_SIZE_FINALIZATION_TIME; + } + } + + // Accelerate to attack speed.. jk, back to normal speed + if (mRotationSpeed < ROTATION_SPEED_NORMAL) { + mRotationSpeed += ROTATION_ACCELERATION_SPEED * dtSec; + } + + // For arcs, this is the "start" + mCurrentAngle += dtSec * mRotationSpeed; + + // Update the sweep angle until it fills entire circle + if (mSweepAngle < 360) { + final float sweepGrowth = mSweepRate * dtSec; + mSweepAngle = mSweepAngle + sweepGrowth; + mSweepRate = mSweepRate + sweepGrowth; + } + if (mSweepAngle > 360) { + mSweepAngle = 360; + mListener.onRingCompleted(mIndex); + } + + // Animate stroke width to final size. + if (tSec < RING_SIZE_FINALIZATION_TIME) { + mCurrentSize = mCurrentSize + mRingAdjustRate * dtSec; + mPaint.setStrokeWidth(mCurrentSize); + } else { + // There should be small to no discontinuity in this if/else + mCurrentSize = FINAL_RING_STROKE_WIDTH; + mPaint.setStrokeWidth(mCurrentSize); + } + + } + + public void draw(Canvas canvas) { + if (mAnimationState != ParticleCollection.STATE_COMPLETE) { + drawDot(canvas); + } else { + drawRing(canvas); + } + } + + // Draws a dot at the current position on the circumference of the path. + private void drawDot(Canvas canvas) { + final float w = mBounds.right - mBounds.exactCenterX() - mBorderWidth; + final float h = mBounds.bottom - mBounds.exactCenterY() - mBorderWidth; + canvas.drawCircle( + mBounds.exactCenterX() + w * (float) Math.cos(mCurrentAngle), + mBounds.exactCenterY() + h * (float) Math.sin(mCurrentAngle), + mCurrentSize, + mPaint); + } + + private void drawRing(Canvas canvas) { + RectF arc = new RectF( + mBorderWidth, mBorderWidth, + mBounds.width() - mBorderWidth, mBounds.height() - mBorderWidth); + Path path = new Path(); + path.arcTo(arc, (float) Math.toDegrees(mCurrentAngle), mSweepAngle); + canvas.drawPath(path, mPaint); + } +} diff --git a/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java index 0da666cc9f6..5be7c5331d3 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollAnimationDrawable.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.face; +import android.animation.TimeAnimator; +import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; @@ -26,16 +28,43 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; -/** - * A drawable containing the circle cutout. - */ -public class FaceEnrollAnimationDrawable extends Drawable { +import com.android.settings.biometrics.BiometricEnrollSidecar; +/** + * A drawable containing the circle cutout as well as the animations. + */ +public class FaceEnrollAnimationDrawable extends Drawable + implements BiometricEnrollSidecar.Listener { + + // Tune this parameter so the UI looks nice - and so that we don't have to draw the animations + // outside our bounds. A fraction of each rotating dot should be overlapping the camera preview. + private static final int BORDER_BOUNDS = 20; + + private final Context mContext; + private final ParticleCollection.Listener mListener; private Rect mBounds; private final Paint mSquarePaint; private final Paint mCircleCutoutPaint; - public FaceEnrollAnimationDrawable() { + private ParticleCollection mParticleCollection; + + private TimeAnimator mTimeAnimator; + + private final ParticleCollection.Listener mAnimationListener + = new ParticleCollection.Listener() { + @Override + public void onEnrolled() { + if (mTimeAnimator != null && mTimeAnimator.isStarted()) { + mTimeAnimator.end(); + mListener.onEnrolled(); + } + } + }; + + public FaceEnrollAnimationDrawable(Context context, ParticleCollection.Listener listener) { + mContext = context; + mListener = listener; + mSquarePaint = new Paint(); mSquarePaint.setColor(Color.WHITE); mSquarePaint.setAntiAlias(true); @@ -46,9 +75,35 @@ public class FaceEnrollAnimationDrawable extends Drawable { mCircleCutoutPaint.setAntiAlias(true); } + @Override + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { + mParticleCollection.onEnrollmentHelp(helpMsgId, helpString); + } + + @Override + public void onEnrollmentError(int errMsgId, CharSequence errString) { + mParticleCollection.onEnrollmentError(errMsgId, errString); + } + + @Override + public void onEnrollmentProgressChange(int steps, int remaining) { + mParticleCollection.onEnrollmentProgressChange(steps, remaining); + } + @Override protected void onBoundsChange(Rect bounds) { mBounds = bounds; + mParticleCollection = + new ParticleCollection(mContext, mAnimationListener, bounds, BORDER_BOUNDS); + + if (mTimeAnimator == null) { + mTimeAnimator = new TimeAnimator(); + mTimeAnimator.setTimeListener((animation, totalTimeMs, deltaTimeMs) -> { + mParticleCollection.update(totalTimeMs, deltaTimeMs); + FaceEnrollAnimationDrawable.this.invalidateSelf(); + }); + mTimeAnimator.start(); + } } @Override @@ -63,7 +118,10 @@ public class FaceEnrollAnimationDrawable extends Drawable { // Clear a circle in the middle for the camera preview canvas.drawCircle(mBounds.exactCenterX(), mBounds.exactCenterY(), - mBounds.height() / 2, mCircleCutoutPaint); + mBounds.height() / 2 - BORDER_BOUNDS, mCircleCutoutPaint); + + // Draw the animation + mParticleCollection.draw(canvas); canvas.restore(); } diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java index 7fac9f6724f..fccb39a20ee 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollEnrolling.java @@ -45,7 +45,14 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { private TextView mErrorText; private Interpolator mLinearOutSlowInInterpolator; private boolean mShouldFinishOnStop = true; - private FaceEnrollPreviewFragment mFaceCameraPreview; + private FaceEnrollPreviewFragment mPreviewFragment; + + private ParticleCollection.Listener mListener = new ParticleCollection.Listener() { + @Override + public void onEnrolled() { + FaceEnrollEnrolling.this.launchFinish(mToken); + } + }; public static class FaceErrorDialog extends BiometricErrorDialog { static FaceErrorDialog newInstance(CharSequence msg, int msgId) { @@ -87,7 +94,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { if (shouldLaunchConfirmLock()) { launchConfirmLock(R.string.security_settings_face_preference_title, - Utils.getFaceManagerOrNull(this).preEnroll()); + Utils.getFingerprintManagerOrNull(this).preEnroll()); mShouldFinishOnStop = false; } else { startEnrollment(); @@ -97,13 +104,14 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { @Override public void startEnrollment() { super.startEnrollment(); - mFaceCameraPreview = (FaceEnrollPreviewFragment) getSupportFragmentManager() + mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager() .findFragmentByTag(TAG_FACE_PREVIEW); - if (mFaceCameraPreview == null) { - mFaceCameraPreview = new FaceEnrollPreviewFragment(); - getSupportFragmentManager().beginTransaction().add(mFaceCameraPreview, TAG_FACE_PREVIEW) + if (mPreviewFragment == null) { + mPreviewFragment = new FaceEnrollPreviewFragment(); + getSupportFragmentManager().beginTransaction().add(mPreviewFragment, TAG_FACE_PREVIEW) .commitAllowingStateLoss(); } + mPreviewFragment.setListener(mListener); } @Override @@ -132,10 +140,11 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { } @Override - public void onEnrollmentHelp(CharSequence helpString) { + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { if (!TextUtils.isEmpty(helpString)) { showError(helpString); } + mPreviewFragment.onEnrollmentHelp(helpMsgId, helpString); } @Override @@ -149,6 +158,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { msgId = R.string.security_settings_face_enroll_error_generic_dialog_message; break; } + mPreviewFragment.onEnrollmentError(errMsgId, errString); showErrorDialog(getText(msgId), errMsgId); } @@ -157,6 +167,8 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling { if (DEBUG) { Log.v(TAG, "Steps: " + steps + " Remaining: " + remaining); } + mPreviewFragment.onEnrollmentProgressChange(steps, remaining); + // TODO: Update the actual animation showError("Steps: " + steps + " Remaining: " + remaining); } diff --git a/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java b/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java index 8bb8b929f36..1861e10a585 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollPreviewFragment.java @@ -38,6 +38,7 @@ import android.widget.ImageView; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.biometrics.BiometricEnrollSidecar; import com.android.settings.core.InstrumentedPreferenceFragment; import java.util.ArrayList; @@ -50,7 +51,8 @@ import java.util.List; * Fragment that contains the logic for showing and controlling the camera preview, circular * overlay, as well as the enrollment animations. */ -public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment { +public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment + implements BiometricEnrollSidecar.Listener { private static final String TAG = "FaceEnrollPreviewFragment"; @@ -65,6 +67,7 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment { private CameraCaptureSession mCaptureSession; private CaptureRequest mPreviewRequest; private Size mPreviewSize; + private ParticleCollection.Listener mListener; // View used to contain the circular cutout and enrollment animation drawable private ImageView mCircleView; @@ -75,6 +78,15 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment { // Texture used for showing the camera preview private FaceSquareTextureView mTextureView; + // Listener sent to the animation drawable + private final ParticleCollection.Listener mAnimationListener + = new ParticleCollection.Listener() { + @Override + public void onEnrolled() { + mListener.onEnrolled(); + } + }; + private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @@ -185,7 +197,7 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment { // Must disable hardware acceleration for this view, otherwise transparency breaks mCircleView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - mAnimationDrawable = new FaceEnrollAnimationDrawable(); + mAnimationDrawable = new FaceEnrollAnimationDrawable(getContext(), mAnimationListener); mCircleView.setImageDrawable(mAnimationDrawable); mCameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); @@ -212,6 +224,25 @@ public class FaceEnrollPreviewFragment extends InstrumentedPreferenceFragment { closeCamera(); } + @Override + public void onEnrollmentError(int errMsgId, CharSequence errString) { + mAnimationDrawable.onEnrollmentError(errMsgId, errString); + } + + @Override + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { + mAnimationDrawable.onEnrollmentHelp(helpMsgId, helpString); + } + + @Override + public void onEnrollmentProgressChange(int steps, int remaining) { + mAnimationDrawable.onEnrollmentProgressChange(steps, remaining); + } + + public void setListener(ParticleCollection.Listener listener) { + mListener = listener; + } + /** * Sets up member variables related to camera. * diff --git a/src/com/android/settings/biometrics/face/ParticleCollection.java b/src/com/android/settings/biometrics/face/ParticleCollection.java new file mode 100644 index 00000000000..399beec3abd --- /dev/null +++ b/src/com/android/settings/biometrics/face/ParticleCollection.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 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.settings.biometrics.face; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Rect; + +import com.android.settings.R; +import com.android.settings.biometrics.BiometricEnrollSidecar; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Class that's used to create, maintain, and update the state of each animation particle. Particles + * should have their colors assigned based on their index. Particles are split into primary and + * secondary types - primary types animate twice as fast during the completion effect. The particles + * are updated/drawn in a special order so that the overlap is correct during the final completion + * effect. + */ +public class ParticleCollection implements BiometricEnrollSidecar.Listener { + + private static final String TAG = "AnimationController"; + + private static final int NUM_PARTICLES = 12; + + public static final int STATE_STARTED = 1; // dots are rotating + public static final int STATE_STOPPED_COLORFUL = 2; // dots are not rotating but colorful + public static final int STATE_STOPPED_GRAY = 3; // dots are not rotating and also gray (error) + public static final int STATE_COMPLETE = 4; // face is enrolled + + private final List mParticleList; + private final List mPrimariesInProgress; // primary particles not done animating yet + private int mState; + private Listener mListener; + + public interface Listener { + void onEnrolled(); + } + + private final AnimationParticle.Listener mParticleListener = new AnimationParticle.Listener() { + @Override + public void onRingCompleted(int index) { + final boolean wasEmpty = mPrimariesInProgress.isEmpty(); + // We can stop the time animator once the three primary particles have finished + for (int i = 0; i < mPrimariesInProgress.size(); i++) { + if (mPrimariesInProgress.get(i).intValue() == index) { + mPrimariesInProgress.remove(i); + break; + } + } + if (mPrimariesInProgress.isEmpty() && !wasEmpty) { + mListener.onEnrolled(); + } + } + }; + + public ParticleCollection(Context context, Listener listener, Rect bounds, int borderWidth) { + mParticleList = new ArrayList<>(); + mListener = listener; + + final List colors = new ArrayList<>(); + final Resources.Theme theme = context.getTheme(); + final Resources resources = context.getResources(); + colors.add(resources.getColor(R.color.face_anim_particle_color_1, theme)); + colors.add(resources.getColor(R.color.face_anim_particle_color_2, theme)); + colors.add(resources.getColor(R.color.face_anim_particle_color_3, theme)); + colors.add(resources.getColor(R.color.face_anim_particle_color_4, theme)); + + // Primary particles expand faster during the completion animation + mPrimariesInProgress = new ArrayList<>(Arrays.asList(0, 4, 8)); + + // Order in which to draw the particles. This is so the final "completion" animation has + // the correct behavior. + final int[] order = {3, 7, 11, 2, 6, 10, 1, 5, 9, 0, 4, 8}; + + for (int i = 0; i < NUM_PARTICLES; i++) { + AnimationParticle particle = new AnimationParticle(context, mParticleListener, bounds, + borderWidth, order[i], NUM_PARTICLES, colors); + if (mPrimariesInProgress.contains(order[i])) { + particle.setAsPrimary(); + } + mParticleList.add(particle); + } + + updateState(STATE_STARTED); + } + + public void update(long t, long dt) { + for (int i = 0; i < mParticleList.size(); i++) { + mParticleList.get(i).update(t, dt); + } + } + + public void draw(Canvas canvas) { + for (int i = 0; i < mParticleList.size(); i++) { + mParticleList.get(i).draw(canvas); + } + } + + private void updateState(int state) { + if (mState != state) { + for (int i = 0; i < mParticleList.size(); i++) { + mParticleList.get(i).updateState(state); + } + mState = state; + } + } + + @Override + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { + + } + + @Override + public void onEnrollmentError(int errMsgId, CharSequence errString) { + + } + + @Override + public void onEnrollmentProgressChange(int steps, int remaining) { + if (remaining == 0) { + updateState(STATE_COMPLETE); + } + } +} diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 38ef2c1b7f4..e6f3b04ae67 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -245,7 +245,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { } @Override - public void onEnrollmentHelp(CharSequence helpString) { + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { if (!TextUtils.isEmpty(helpString)) { mErrorText.removeCallbacks(mTouchAgainRunnable); showError(helpString); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java index 927b5eb905e..c104eb3de94 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java @@ -94,7 +94,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase { } @Override - public void onEnrollmentHelp(CharSequence helpString) { + public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { } @Override diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java index a37ab5a246f..18f6999cf6d 100644 --- a/src/com/android/settings/dashboard/DashboardSummary.java +++ b/src/com/android/settings/dashboard/DashboardSummary.java @@ -123,7 +123,6 @@ public class DashboardSummary extends InstrumentedFragment mSummaryLoader = new SummaryLoader(activity, CategoryKey.CATEGORY_HOMEPAGE); - mConditionManager = ConditionManager.get(activity, false); if (com.android.settings.homepage.conditional.v2.ConditionManager.isEnabled(activity)) { mConditionManager = null; mConditionManager2 = diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java index 73d83e15db5..2a53f9f8e1b 100644 --- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java @@ -28,6 +28,7 @@ import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; +import com.android.settingslib.RestrictedPreference; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; @@ -76,6 +77,11 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo @Override public void updateState(Preference preference) { + final RestrictedPreference restrictedPreference = (RestrictedPreference) preference; + if (restrictedPreference.isDisabledByAdmin()) { + // If disabled, let RestrictedPreference handle it and do nothing here + return; + } final int mode = mAppOpsManager .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage); final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage); diff --git a/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java b/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java index ef564feef09..b6a67af3015 100644 --- a/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java +++ b/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBase.java @@ -25,6 +25,7 @@ import android.provider.Settings; import com.android.settings.R; +@Deprecated public abstract class AbnormalRingerConditionBase extends Condition { private final IntentFilter mFilter; @@ -45,7 +46,7 @@ public abstract class AbnormalRingerConditionBase extends Condition { @Override public CharSequence[] getActions() { - return new CharSequence[] { + return new CharSequence[]{ mManager.getContext().getText(R.string.condition_device_muted_action_turn_on_sound) }; } diff --git a/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java b/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java index 9d21e438f7b..069d316d81e 100644 --- a/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java +++ b/src/com/android/settings/homepage/conditional/AirplaneModeCondition.java @@ -28,6 +28,10 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settingslib.WirelessUtils; +/** + * @deprecated in favor of {@link AirplaneModeConditionCard}. + */ +@Deprecated public class AirplaneModeCondition extends Condition { public static String TAG = "APM_Condition"; diff --git a/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java b/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java index 84ae9241412..4e374ef1c6a 100644 --- a/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java +++ b/src/com/android/settings/homepage/conditional/BackgroundDataCondition.java @@ -23,6 +23,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settings.R; import com.android.settings.Settings; +@Deprecated public class BackgroundDataCondition extends Condition { public BackgroundDataCondition(ConditionManager manager) { diff --git a/src/com/android/settings/homepage/conditional/BatterySaverCondition.java b/src/com/android/settings/homepage/conditional/BatterySaverCondition.java index 1301b3de252..f36dcb7cf8d 100644 --- a/src/com/android/settings/homepage/conditional/BatterySaverCondition.java +++ b/src/com/android/settings/homepage/conditional/BatterySaverCondition.java @@ -26,6 +26,10 @@ import com.android.settings.fuelgauge.BatterySaverReceiver; import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; import com.android.settingslib.fuelgauge.BatterySaverUtils; +/** + * @deprecated in favor of {@link BatterySaverConditionCard} + */ +@Deprecated public class BatterySaverCondition extends Condition implements BatterySaverReceiver.BatterySaverListener { diff --git a/src/com/android/settings/homepage/conditional/CellularDataCondition.java b/src/com/android/settings/homepage/conditional/CellularDataCondition.java index 7b382a7cf28..40729128c57 100644 --- a/src/com/android/settings/homepage/conditional/CellularDataCondition.java +++ b/src/com/android/settings/homepage/conditional/CellularDataCondition.java @@ -23,6 +23,7 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.settings.R; import com.android.settings.Settings; +@Deprecated public class CellularDataCondition extends Condition { private final Receiver mReceiver; diff --git a/src/com/android/settings/homepage/conditional/DndCondition.java b/src/com/android/settings/homepage/conditional/DndCondition.java index 3e8052b4808..d2386136ebe 100644 --- a/src/com/android/settings/homepage/conditional/DndCondition.java +++ b/src/com/android/settings/homepage/conditional/DndCondition.java @@ -33,6 +33,10 @@ import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; import com.android.settings.notification.ZenModeSettings; +/** + * @deprecated in favor of {@link com.android.settings.homepage.conditional.v2.DndConditionCard} + */ +@Deprecated public class DndCondition extends Condition { private static final String TAG = "DndCondition"; diff --git a/src/com/android/settings/homepage/conditional/HotspotCondition.java b/src/com/android/settings/homepage/conditional/HotspotCondition.java index 7212e841701..6934d0f304e 100644 --- a/src/com/android/settings/homepage/conditional/HotspotCondition.java +++ b/src/com/android/settings/homepage/conditional/HotspotCondition.java @@ -33,6 +33,7 @@ import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +@Deprecated public class HotspotCondition extends Condition { private final WifiManager mWifiManager; @@ -95,7 +96,7 @@ public class HotspotCondition extends Condition { UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId())) { return new CharSequence[0]; } - return new CharSequence[] {context.getString(R.string.condition_turn_off)}; + return new CharSequence[]{context.getString(R.string.condition_turn_off)}; } @Override diff --git a/src/com/android/settings/homepage/conditional/NightDisplayCondition.java b/src/com/android/settings/homepage/conditional/NightDisplayCondition.java index 95769734713..384f593ec4f 100644 --- a/src/com/android/settings/homepage/conditional/NightDisplayCondition.java +++ b/src/com/android/settings/homepage/conditional/NightDisplayCondition.java @@ -25,6 +25,10 @@ import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; import com.android.settings.display.NightDisplaySettings; +/** + * @deprecated in favor of {@link NightDisplayConditionController} + */ +@Deprecated public final class NightDisplayCondition extends Condition implements ColorDisplayController.Callback { diff --git a/src/com/android/settings/homepage/conditional/RingerMutedCondition.java b/src/com/android/settings/homepage/conditional/RingerMutedCondition.java index 740e6e48bab..d3e618e5b59 100644 --- a/src/com/android/settings/homepage/conditional/RingerMutedCondition.java +++ b/src/com/android/settings/homepage/conditional/RingerMutedCondition.java @@ -26,6 +26,7 @@ import android.provider.Settings; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +@Deprecated public class RingerMutedCondition extends AbnormalRingerConditionBase { private final NotificationManager mNotificationManager; diff --git a/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java b/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java index ea91c0ef28b..92667c2117c 100644 --- a/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java +++ b/src/com/android/settings/homepage/conditional/RingerVibrateCondition.java @@ -22,6 +22,7 @@ import android.media.AudioManager; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +@Deprecated public class RingerVibrateCondition extends AbnormalRingerConditionBase { RingerVibrateCondition(ConditionManager manager) { diff --git a/src/com/android/settings/homepage/conditional/WorkModeCondition.java b/src/com/android/settings/homepage/conditional/WorkModeCondition.java index 9e467faf446..31266a4b512 100644 --- a/src/com/android/settings/homepage/conditional/WorkModeCondition.java +++ b/src/com/android/settings/homepage/conditional/WorkModeCondition.java @@ -29,6 +29,7 @@ import com.android.settings.Settings; import java.util.List; +@Deprecated public class WorkModeCondition extends Condition { private UserManager mUm; diff --git a/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionController.java b/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionController.java new file mode 100644 index 00000000000..1cc62b944c2 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionController.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.provider.Settings; + +import java.util.Objects; + +public abstract class AbnormalRingerConditionController implements ConditionalCardController { + + private static final IntentFilter FILTER = + new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + + protected final AudioManager mAudioManager; + private final Context mAppContext; + private final ConditionManager mConditionManager; + private final RingerModeChangeReceiver mReceiver; + + public AbnormalRingerConditionController(Context appContext, ConditionManager manager) { + mAppContext = appContext; + mConditionManager = manager; + mAudioManager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE); + mReceiver = new RingerModeChangeReceiver(); + } + + @Override + public void onPrimaryClick(Context context) { + context.startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS)); + } + + @Override + public void onActionClick() { + mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL); + mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0 /* flags */); + } + + @Override + public void startMonitoringStateChange() { + mAppContext.registerReceiver(mReceiver, FILTER); + } + + @Override + public void stopMonitoringStateChange() { + mAppContext.unregisterReceiver(mReceiver); + } + + class RingerModeChangeReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { + mConditionManager.onConditionChanged(); + } + } + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionCard.java b/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionCard.java new file mode 100644 index 00000000000..99055a749fa --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class AirplaneModeConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public AirplaneModeConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return AirplaneModeConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_off); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_AIRPLANE_MODE; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_airplane); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_airplane_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_airplane_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionController.java b/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionController.java new file mode 100644 index 00000000000..2d200ecdba6 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/AirplaneModeConditionController.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.provider.Settings; + +import com.android.settingslib.WirelessUtils; + +import java.util.Objects; + +public class AirplaneModeConditionController implements ConditionalCardController { + + static final int ID = Objects.hash("AirplaneModeConditionController"); + + private static final IntentFilter AIRPLANE_MODE_FILTER = + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + + private final ConditionManager mConditionManager; + private final Context mAppContext; + private final Receiver mReceiver; + + public AirplaneModeConditionController(Context appContext, ConditionManager conditionManager) { + mAppContext = appContext; + mConditionManager = conditionManager; + mReceiver = new Receiver(); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return WirelessUtils.isAirplaneModeOn(mAppContext); + } + + @Override + public void onPrimaryClick(Context context) { + context.startActivity( + new Intent(Settings.ACTION_WIRELESS_SETTINGS)); + } + + @Override + public void onActionClick() { + ConnectivityManager.from(mAppContext).setAirplaneMode(false); + } + + @Override + public void startMonitoringStateChange() { + mAppContext.registerReceiver(mReceiver, AIRPLANE_MODE_FILTER); + } + + @Override + public void stopMonitoringStateChange() { + mAppContext.unregisterReceiver(mReceiver); + } + + public class Receiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) { + mConditionManager.onConditionChanged(); + } + } + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionCard.java b/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionCard.java new file mode 100644 index 00000000000..3ea31130d8d --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class BackgroundDataConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public BackgroundDataConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return BackgroundDataConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_off); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_BACKGROUND_DATA; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_data_saver); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_bg_data_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_bg_data_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionController.java b/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionController.java new file mode 100644 index 00000000000..3649607879b --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionController.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.content.Intent; +import android.net.NetworkPolicyManager; + +import com.android.settings.Settings; + +import java.util.Objects; + +public class BackgroundDataConditionController implements ConditionalCardController { + static final int ID = Objects.hash("BackgroundDataConditionController"); + + private final Context mAppContext; + + public BackgroundDataConditionController(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return NetworkPolicyManager.from(mAppContext).getRestrictBackground(); + } + + @Override + public void onPrimaryClick(Context context) { + context.startActivity(new Intent(context, Settings.DataUsageSummaryActivity.class)); + } + + @Override + public void onActionClick() { + NetworkPolicyManager.from(mAppContext).setRestrictBackground(false); + } + + @Override + public void startMonitoringStateChange() { + + } + + @Override + public void stopMonitoringStateChange() { + + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionCard.java b/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionCard.java new file mode 100644 index 00000000000..c30715f5bd4 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class BatterySaverConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public BatterySaverConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return BatterySaverConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_off); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_BATTERY_SAVER; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_battery_saver_accent_24dp); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_battery_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_battery_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionController.java b/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionController.java new file mode 100644 index 00000000000..167dc12c614 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionController.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.os.PowerManager; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.fuelgauge.BatterySaverReceiver; +import com.android.settings.fuelgauge.batterysaver.BatterySaverSettings; +import com.android.settingslib.fuelgauge.BatterySaverUtils; + +import java.util.Objects; + +public class BatterySaverConditionController implements ConditionalCardController, + BatterySaverReceiver.BatterySaverListener { + static final int ID = Objects.hash("BatterySaverConditionController"); + + private final Context mAppContext; + private final ConditionManager mConditionManager; + private final BatterySaverReceiver mReceiver; + private final PowerManager mPowerManager; + + public BatterySaverConditionController(Context appContext, ConditionManager conditionManager) { + mAppContext = appContext; + mConditionManager = conditionManager; + mPowerManager = appContext.getSystemService(PowerManager.class); + mReceiver = new BatterySaverReceiver(appContext); + mReceiver.setBatterySaverListener(this); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return mPowerManager.isPowerSaveMode(); + } + + @Override + public void onPrimaryClick(Context context) { + new SubSettingLauncher(context) + .setDestination(BatterySaverSettings.class.getName()) + .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY) + .setTitleRes(R.string.battery_saver) + .launch(); + } + + @Override + public void onActionClick() { + BatterySaverUtils.setPowerSaveMode(mAppContext, false, + /*needFirstTimeWarning*/ false); + } + + @Override + public void startMonitoringStateChange() { + mReceiver.setListening(true); + } + + @Override + public void stopMonitoringStateChange() { + mReceiver.setListening(false); + } + + @Override + public void onPowerSaveModeChanged() { + mConditionManager.onConditionChanged(); + } + + @Override + public void onBatteryChanged(boolean pluggedIn) { + + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/CellularDataConditionCard.java b/src/com/android/settings/homepage/conditional/v2/CellularDataConditionCard.java new file mode 100644 index 00000000000..c09e75a4f62 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/CellularDataConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class CellularDataConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public CellularDataConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return CellularDataConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_on); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_CELLULAR_DATA; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_cellular_off); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_cellular_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_cellular_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/CellularDataConditionController.java b/src/com/android/settings/homepage/conditional/v2/CellularDataConditionController.java new file mode 100644 index 00000000000..d23dc343501 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/CellularDataConditionController.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.telephony.TelephonyManager; + +import com.android.internal.telephony.TelephonyIntents; +import com.android.settings.Settings; + +import java.util.Objects; + +public class CellularDataConditionController implements ConditionalCardController { + + static final int ID = Objects.hash("CellularDataConditionController"); + + private static final IntentFilter DATA_CONNECTION_FILTER = + new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); + + private final Context mAppContext; + private final ConditionManager mConditionManager; + private final Receiver mReceiver; + private final TelephonyManager mTelephonyManager; + private final ConnectivityManager mConnectivityManager; + + public CellularDataConditionController(Context appContext, ConditionManager conditionManager) { + mAppContext = appContext; + mConditionManager = conditionManager; + mReceiver = new Receiver(); + mConnectivityManager = appContext.getSystemService( + ConnectivityManager.class); + mTelephonyManager = appContext.getSystemService(TelephonyManager.class); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + if (!mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) + || mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) { + return false; + } + return !mTelephonyManager.isDataEnabled(); + } + + @Override + public void onPrimaryClick(Context context) { + context.startActivity(new Intent(context, + Settings.DataUsageSummaryActivity.class)); + } + + @Override + public void onActionClick() { + mTelephonyManager.setDataEnabled(true); + } + + @Override + public void startMonitoringStateChange() { + mAppContext.registerReceiver(mReceiver, DATA_CONNECTION_FILTER); + } + + @Override + public void stopMonitoringStateChange() { + mAppContext.unregisterReceiver(mReceiver); + } + + public class Receiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals( + intent.getAction())) { + mConditionManager.onConditionChanged(); + } + } + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/ConditionManager.java b/src/com/android/settings/homepage/conditional/v2/ConditionManager.java index e6530d31ca2..ff5d20a868f 100644 --- a/src/com/android/settings/homepage/conditional/v2/ConditionManager.java +++ b/src/com/android/settings/homepage/conditional/v2/ConditionManager.java @@ -141,9 +141,28 @@ public class ConditionManager { private void initCandidates() { // Initialize controllers first. + mCardControllers.add(new AirplaneModeConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new BackgroundDataConditionController(mAppContext)); + mCardControllers.add(new BatterySaverConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new CellularDataConditionController(mAppContext, this /* manager */)); mCardControllers.add(new DndConditionCardController(mAppContext, this /* manager */)); + mCardControllers.add(new HotspotConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new NightDisplayConditionController(mAppContext)); + mCardControllers.add(new RingerVibrateConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new RingerMutedConditionController(mAppContext, this /* manager */)); + mCardControllers.add(new WorkModeConditionController(mAppContext)); // Initialize ui model later. UI model depends on controller. + mCandidates.add(new AirplaneModeConditionCard(mAppContext)); + mCandidates.add(new BackgroundDataConditionCard(mAppContext)); + mCandidates.add(new BatterySaverConditionCard(mAppContext)); + mCandidates.add(new CellularDataConditionCard(mAppContext)); mCandidates.add(new DndConditionCard(mAppContext, this /* manager */)); + mCandidates.add(new HotspotConditionCard(mAppContext, this /* manager */)); + mCandidates.add(new NightDisplayConditionCard(mAppContext)); + mCandidates.add(new RingerMutedConditionCard(mAppContext)); + mCandidates.add(new RingerVibrateConditionCard(mAppContext)); + mCandidates.add(new WorkModeConditionCard(mAppContext)); + } } diff --git a/src/com/android/settings/homepage/conditional/v2/DndConditionCardController.java b/src/com/android/settings/homepage/conditional/v2/DndConditionCardController.java index 9e302571c53..9e51ef95efe 100644 --- a/src/com/android/settings/homepage/conditional/v2/DndConditionCardController.java +++ b/src/com/android/settings/homepage/conditional/v2/DndConditionCardController.java @@ -78,7 +78,7 @@ public class DndConditionCardController implements ConditionalCardController { public void onPrimaryClick(Context context) { new SubSettingLauncher(context) .setDestination(ZenModeSettings.class.getName()) - .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY) + .setSourceMetricsCategory(MetricsProto.MetricsEvent.SETTINGS_HOMEPAGE) .setTitleRes(R.string.zen_mode_settings_title) .launch(); } diff --git a/src/com/android/settings/homepage/conditional/v2/HotspotConditionCard.java b/src/com/android/settings/homepage/conditional/v2/HotspotConditionCard.java new file mode 100644 index 00000000000..63b5034729d --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/HotspotConditionCard.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settingslib.RestrictedLockUtils; + +public class HotspotConditionCard implements ConditionalCard { + + private final Context mAppContext; + private final ConditionManager mConditionManager; + + public HotspotConditionCard(Context appContext, ConditionManager manager) { + mAppContext = appContext; + mConditionManager = manager; + } + + @Override + public long getId() { + return HotspotConditionController.ID; + } + + @Override + public CharSequence getActionText() { + if (RestrictedLockUtils.hasBaseUserRestriction(mAppContext, + UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId())) { + return null; + } + return mAppContext.getText(R.string.condition_turn_off); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_HOTSPOT; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_hotspot); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_hotspot_title); + } + + @Override + public CharSequence getSummary() { + final HotspotConditionController controller = mConditionManager.getController(getId()); + return controller.getSummary(); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/HotspotConditionController.java b/src/com/android/settings/homepage/conditional/v2/HotspotConditionController.java new file mode 100644 index 00000000000..56e95d1c4ed --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/HotspotConditionController.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.TetherSettings; +import com.android.settings.core.SubSettingLauncher; +import com.android.settingslib.RestrictedLockUtils; + +import java.util.Objects; + +public class HotspotConditionController implements ConditionalCardController { + static final int ID = Objects.hash("HotspotConditionController"); + + private static final IntentFilter WIFI_AP_STATE_FILTER = + new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + + private final Context mAppContext; + private final ConditionManager mConditionManager; + private final WifiManager mWifiManager; + private final Receiver mReceiver; + + + public HotspotConditionController(Context appContext, ConditionManager conditionManager) { + mAppContext = appContext; + mConditionManager = conditionManager; + mWifiManager = appContext.getSystemService(WifiManager.class); + mReceiver = new Receiver(); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return mWifiManager.isWifiApEnabled(); + } + + @Override + public void onPrimaryClick(Context context) { + new SubSettingLauncher(context) + .setDestination(TetherSettings.class.getName()) + .setSourceMetricsCategory(MetricsProto.MetricsEvent.DASHBOARD_SUMMARY) + .setTitleRes(R.string.tether_settings_title_all) + .launch(); + } + + @Override + public void onActionClick() { + final RestrictedLockUtils.EnforcedAdmin admin = + RestrictedLockUtils.checkIfRestrictionEnforced( + mAppContext, UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()); + if (admin != null) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mAppContext, admin); + } else { + ConnectivityManager cm = (ConnectivityManager) mAppContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + cm.stopTethering(ConnectivityManager.TETHERING_WIFI); + } + } + + @Override + public void startMonitoringStateChange() { + mAppContext.registerReceiver(mReceiver, WIFI_AP_STATE_FILTER); + } + + @Override + public void stopMonitoringStateChange() { + mAppContext.unregisterReceiver(mReceiver); + } + + public CharSequence getSummary() { + return mAppContext.getString(R.string.condition_hotspot_summary, getSsid()); + } + + private CharSequence getSsid() { + WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration(); + if (wifiConfig == null) { + return mAppContext.getText( + com.android.internal.R.string.wifi_tether_configure_ssid_default); + } else { + return wifiConfig.SSID; + } + } + + public class Receiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(intent.getAction())) { + mConditionManager.onConditionChanged(); + } + } + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionCard.java b/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionCard.java new file mode 100644 index 00000000000..b08f8e721e5 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class NightDisplayConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public NightDisplayConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return NightDisplayConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_off); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_NIGHT_DISPLAY; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_settings_night_display); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_night_display_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_night_display_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionController.java b/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionController.java new file mode 100644 index 00000000000..0d612188c5e --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/NightDisplayConditionController.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; + +import com.android.internal.app.ColorDisplayController; +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.display.NightDisplaySettings; + +import java.util.Objects; + +public class NightDisplayConditionController implements ConditionalCardController, + ColorDisplayController.Callback { + static final int ID = Objects.hash("NightDisplayConditionController"); + + private final ColorDisplayController mController; + + public NightDisplayConditionController(Context appContext) { + mController = new ColorDisplayController(appContext); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return mController.isActivated(); + } + + @Override + public void onPrimaryClick(Context context) { + new SubSettingLauncher(context) + .setDestination(NightDisplaySettings.class.getName()) + .setSourceMetricsCategory(MetricsProto.MetricsEvent.SETTINGS_HOMEPAGE) + .setTitleRes(R.string.night_display_title) + .launch(); + } + + @Override + public void onActionClick() { + mController.setActivated(false); + } + + @Override + public void startMonitoringStateChange() { + mController.setListener(this); + } + + @Override + public void stopMonitoringStateChange() { + mController.setListener(null); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionCard.java b/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionCard.java new file mode 100644 index 00000000000..c9a1ba062a7 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class RingerMutedConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public RingerMutedConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return RingerMutedConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_device_muted_action_turn_on_sound); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_DEVICE_MUTED; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_notifications_off_24dp); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_device_muted_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_device_muted_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionController.java b/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionController.java new file mode 100644 index 00000000000..19850ae1c43 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/RingerMutedConditionController.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import static android.content.Context.NOTIFICATION_SERVICE; + +import android.app.NotificationManager; +import android.content.Context; +import android.media.AudioManager; +import android.provider.Settings; + +import java.util.Objects; + +public class RingerMutedConditionController extends AbnormalRingerConditionController { + static final int ID = Objects.hash("RingerMutedConditionController"); + + private final NotificationManager mNotificationManager; + + public RingerMutedConditionController(Context appContext, ConditionManager conditionManager) { + super(appContext, conditionManager); + mNotificationManager = + (NotificationManager) appContext.getSystemService(NOTIFICATION_SERVICE); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + int zen = Settings.Global.ZEN_MODE_OFF; + if (mNotificationManager != null) { + zen = mNotificationManager.getZenMode(); + } + final boolean zenModeEnabled = zen != Settings.Global.ZEN_MODE_OFF; + final boolean isSilent = + mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT; + return isSilent && !zenModeEnabled; + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionCard.java b/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionCard.java new file mode 100644 index 00000000000..3ca3a4de552 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionCard.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class RingerVibrateConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public RingerVibrateConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return RingerVibrateConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_device_muted_action_turn_on_sound); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_DEVICE_VIBRATE; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_volume_ringer_vibrate); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_device_vibrate_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_device_vibrate_summary); + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionController.java b/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionController.java new file mode 100644 index 00000000000..d2d32447053 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/RingerVibrateConditionController.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.media.AudioManager; + +import java.util.Objects; + +public class RingerVibrateConditionController extends AbnormalRingerConditionController { + static final int ID = Objects.hash("RingerVibrateConditionController"); + + public RingerVibrateConditionController(Context appContext, ConditionManager conditionManager) { + super(appContext, conditionManager); + + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + return mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE; + } +} diff --git a/src/com/android/settings/homepage/conditional/v2/WorkModeConditionCard.java b/src/com/android/settings/homepage/conditional/v2/WorkModeConditionCard.java new file mode 100644 index 00000000000..10bcd6d7a11 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/WorkModeConditionCard.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; + +public class WorkModeConditionCard implements ConditionalCard { + + private final Context mAppContext; + + public WorkModeConditionCard(Context appContext) { + mAppContext = appContext; + } + + @Override + public long getId() { + return WorkModeConditionController.ID; + } + + @Override + public CharSequence getActionText() { + return mAppContext.getText(R.string.condition_turn_on); + } + + @Override + public int getMetricsConstant() { + return MetricsProto.MetricsEvent.SETTINGS_CONDITION_WORK_MODE; + } + + @Override + public Drawable getIcon() { + return mAppContext.getDrawable(R.drawable.ic_signal_workmode_enable); + } + + @Override + public CharSequence getTitle() { + return mAppContext.getText(R.string.condition_work_title); + } + + @Override + public CharSequence getSummary() { + return mAppContext.getText(R.string.condition_work_summary); + } + +} diff --git a/src/com/android/settings/homepage/conditional/v2/WorkModeConditionController.java b/src/com/android/settings/homepage/conditional/v2/WorkModeConditionController.java new file mode 100644 index 00000000000..73689c7f557 --- /dev/null +++ b/src/com/android/settings/homepage/conditional/v2/WorkModeConditionController.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.settings.Settings; + +import java.util.List; +import java.util.Objects; + +public class WorkModeConditionController implements ConditionalCardController { + + static final int ID = Objects.hash("WorkModeConditionController"); + + private final Context mAppContext; + private final UserManager mUm; + private UserHandle mUserHandle; + + public WorkModeConditionController(Context appContext) { + mAppContext = appContext; + mUm = mAppContext.getSystemService(UserManager.class); + } + + @Override + public long getId() { + return ID; + } + + @Override + public boolean isDisplayable() { + updateUserHandle(); + return mUserHandle != null && mUm.isQuietModeEnabled(mUserHandle); + } + + @Override + public void onPrimaryClick(Context context) { + context.startActivity(new Intent(context, + Settings.AccountDashboardActivity.class)); + } + + @Override + public void onActionClick() { + if (mUserHandle != null) { + mUm.requestQuietModeEnabled(false, mUserHandle); + } + } + + @Override + public void startMonitoringStateChange() { + + } + + @Override + public void stopMonitoringStateChange() { + + } + + private void updateUserHandle() { + List profiles = mUm.getProfiles(UserHandle.myUserId()); + final int profilesCount = profiles.size(); + mUserHandle = null; + for (int i = 0; i < profilesCount; i++) { + UserInfo userInfo = profiles.get(i); + if (userInfo.isManagedProfile()) { + // We assume there's only one managed profile, otherwise UI needs to change. + mUserHandle = userInfo.getUserHandle(); + break; + } + } + } +} diff --git a/src/com/android/settings/network/ApnSettings.java b/src/com/android/settings/network/ApnSettings.java index 4661f4e6cc4..25734d64173 100755 --- a/src/com/android/settings/network/ApnSettings.java +++ b/src/com/android/settings/network/ApnSettings.java @@ -105,6 +105,7 @@ public class ApnSettings extends RestrictedSettingsFragment implements private RestoreApnProcessHandler mRestoreApnProcessHandler; private HandlerThread mRestoreDefaultApnThread; private SubscriptionInfo mSubscriptionInfo; + private int mSubId; private UiccController mUiccController; private String mMvnoType; private String mMvnoMatchData; @@ -140,6 +141,13 @@ public class ApnSettings extends RestrictedSettingsFragment implements } else if(intent.getAction().equals( TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) { if (!mRestoreDefaultApnMode) { + int extraSubId = intent.getIntExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + if (extraSubId != mSubId) { + // subscription has changed + mSubId = extraSubId; + mSubscriptionInfo = getSubscriptionInfo(mSubId); + } fillList(); } } @@ -164,7 +172,7 @@ public class ApnSettings extends RestrictedSettingsFragment implements public void onCreate(Bundle icicle) { super.onCreate(icicle); final Activity activity = getActivity(); - final int subId = activity.getIntent().getIntExtra(SUB_ID, + mSubId = activity.getIntent().getIntExtra(SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); mIntentFilter = new IntentFilter( @@ -173,7 +181,7 @@ public class ApnSettings extends RestrictedSettingsFragment implements setIfOnlyAvailableForAdmins(true); - mSubscriptionInfo = SubscriptionManager.from(activity).getActiveSubscriptionInfo(subId); + mSubscriptionInfo = getSubscriptionInfo(mSubId); mUiccController = UiccController.getInstance(); CarrierConfigManager configManager = (CarrierConfigManager) @@ -254,6 +262,10 @@ public class ApnSettings extends RestrictedSettingsFragment implements return null; } + private SubscriptionInfo getSubscriptionInfo(int subId) { + return SubscriptionManager.from(getActivity()).getActiveSubscriptionInfo(subId); + } + private void fillList() { final TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); final int subId = mSubscriptionInfo != null ? mSubscriptionInfo.getSubscriptionId() diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 16908d62a1a..47f3ceda391 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -99,6 +99,11 @@ public class WifiTetherSettings extends RestrictedDashboardFragment super.onAttach(context); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mTetherChangeReceiver = new TetherChangeReceiver(); + + mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); + mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); + mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); + mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); } @Override @@ -140,18 +145,17 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @Override protected List createPreferenceControllers(Context context) { - final List controllers = new ArrayList<>(); - mSSIDPreferenceController = new WifiTetherSSIDPreferenceController(context, this); - mSecurityPreferenceController = new WifiTetherSecurityPreferenceController(context, this); - mPasswordPreferenceController = new WifiTetherPasswordPreferenceController(context, this); - mApBandPreferenceController = new WifiTetherApBandPreferenceController(context, this); + return buildPreferenceControllers(context, this::onTetherConfigUpdated); + } + + private static List buildPreferenceControllers(Context context, + WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { + final List controllers = new ArrayList<>(); + controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); + controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); + controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); + controllers.add(new WifiTetherApBandPreferenceController(context, listener)); - controllers.add(mSSIDPreferenceController); - controllers.add(mSecurityPreferenceController); - controllers.add(mPasswordPreferenceController); - controllers.add(mApBandPreferenceController); - controllers.add( - new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); return controllers; } @@ -227,6 +231,12 @@ public class WifiTetherSettings extends RestrictedDashboardFragment keys.add(KEY_WIFI_TETHER_SCREEN); return keys; } + + @Override + public List createPreferenceControllers( + Context context) { + return buildPreferenceControllers(context, null /* listener */); + } }; @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java index 1a3155d2760..6b52b650879 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java @@ -17,10 +17,13 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -38,6 +41,7 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowFragment; +import com.android.settingslib.RestrictedPreference; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; import org.junit.Before; @@ -49,8 +53,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import androidx.preference.Preference; - @RunWith(SettingsRobolectricTestRunner.class) @Config(shadows = {SettingsShadowResources.SettingsShadowTheme.class, ShadowFragment.class}) public class BackgroundActivityPreferenceControllerTest { @@ -80,7 +82,7 @@ public class BackgroundActivityPreferenceControllerTest { @Mock private PowerWhitelistBackend mPowerWhitelistBackend; private BackgroundActivityPreferenceController mController; - private Preference mPreference; + private RestrictedPreference mPreference; private Context mShadowContext; private BatteryUtils mBatteryUtils; @@ -109,7 +111,7 @@ public class BackgroundActivityPreferenceControllerTest { mBatteryUtils = spy(new BatteryUtils(mShadowContext)); doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt()); - mPreference = new Preference(mShadowContext); + mPreference = spy(new RestrictedPreference(mShadowContext, null /* attrs */)); mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY); mController = spy(new BackgroundActivityPreferenceController( mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend)); @@ -118,7 +120,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testHandlePreferenceTreeClick_restrictApp_showDialog() { + public void handlePreferenceTreeClick_restrictApp_showDialog() { doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager) .checkOpNoThrow(anyInt(), anyInt(), anyString()); @@ -128,7 +130,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testHandlePreferenceTreeClick_unRestrictApp_showDialog() { + public void handlePreferenceTreeClick_unRestrictApp_showDialog() { doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager) .checkOpNoThrow(anyInt(), anyInt(), anyString()); @@ -138,7 +140,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateState_noError_setEnabled() { + public void updateState_noError_setEnabled() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED); @@ -149,7 +151,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateState_whitelisted() { + public void updateState_whitelisted() { when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true); mController.updateState(mPreference); assertThat(mPreference.isEnabled()).isFalse(); @@ -158,7 +160,16 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateSummary_modeError_showSummaryDisabled() { + public void updateState_disabledByAdmin_doNothing() { + doReturn(true).when(mPreference).isDisabledByAdmin(); + + mController.updateState(mPreference); + + verify(mPreference, never()).setEnabled(anyBoolean()); + } + + @Test + public void updateSummary_modeError_showSummaryDisabled() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ERRORED); final CharSequence expectedSummary = mShadowContext.getText( @@ -169,7 +180,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateSummary_modeDefault_showNotRestricted() { + public void updateSummary_modeDefault_showNotRestricted() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT); @@ -179,7 +190,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testUpdateSummary_modeIgnored_showRestricted() { + public void updateSummary_modeIgnored_showRestricted() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK, LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED); @@ -189,7 +200,7 @@ public class BackgroundActivityPreferenceControllerTest { } @Test - public void testIsAvailable_ReturnTrue() { + public void isAvailable_ReturnTrue() { assertThat(mController.isAvailable()).isTrue(); } } diff --git a/tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionControllerBaseTest.java similarity index 61% rename from tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java rename to tests/robotests/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionControllerBaseTest.java index efc5ceab889..dc06710335e 100644 --- a/tests/robotests/src/com/android/settings/homepage/conditional/AbnormalRingerConditionBaseTest.java +++ b/tests/robotests/src/com/android/settings/homepage/conditional/v2/AbnormalRingerConditionControllerBaseTest.java @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.settings.homepage.conditional; +package com.android.settings.homepage.conditional.v2; -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.media.AudioManager; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -35,7 +33,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) -public class AbnormalRingerConditionBaseTest { +public class AbnormalRingerConditionControllerBaseTest { @Mock private ConditionManager mConditionManager; @@ -47,53 +45,39 @@ public class AbnormalRingerConditionBaseTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - when(mConditionManager.getContext()).thenReturn(mContext); - mCondition = new TestCondition(mConditionManager); + + mCondition = new TestCondition(mContext, mConditionManager); } @Test - public void newInstance_shouldMonitorRingerStateChangeBroadcast() { + public void startMonitor_shouldMonitorRingerStateChangeBroadcast() { final Intent broadcast1 = new Intent("foo.bar.action"); final Intent broadcast2 = new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); + mCondition.startMonitoringStateChange(); + mContext.sendBroadcast(broadcast1); - assertThat(mCondition.mRefreshCalled).isFalse(); + verify(mConditionManager, never()).onConditionChanged(); mContext.sendBroadcast(broadcast2); - assertThat(mCondition.mRefreshCalled).isTrue(); + verify(mConditionManager).onConditionChanged(); } - private static class TestCondition extends AbnormalRingerConditionBase { - private boolean mRefreshCalled; + private static class TestCondition extends AbnormalRingerConditionController { - TestCondition(ConditionManager manager) { - super(manager); + public TestCondition(Context appContext, ConditionManager conditionManager) { + super(appContext, conditionManager); } - @Override - public void refreshState() { - mRefreshCalled = true; - } @Override - public int getMetricsConstant() { + public long getId() { return 0; } @Override - public Drawable getIcon() { - return null; + public boolean isDisplayable() { + return false; } - - @Override - public CharSequence getTitle() { - return null; - } - - @Override - public CharSequence getSummary() { - return null; - } - } } diff --git a/tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionControllerTest.java similarity index 79% rename from tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java rename to tests/robotests/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionControllerTest.java index 289fa7c22ba..b12d786067a 100644 --- a/tests/robotests/src/com/android/settings/homepage/conditional/BackgroundDataConditionTest.java +++ b/tests/robotests/src/com/android/settings/homepage/conditional/v2/BackgroundDataConditionControllerTest.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.settings.homepage.conditional; +package com.android.settings.homepage.conditional.v2; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; @@ -31,30 +30,25 @@ 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 org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) -public class BackgroundDataConditionTest { - @Mock - private ConditionManager mConditionManager; - +public class BackgroundDataConditionControllerTest { private Context mContext; + private BackgroundDataConditionController mController; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - when(mConditionManager.getContext()).thenReturn(mContext); + mController = new BackgroundDataConditionController(mContext); } @Test public void onPrimaryClick_shouldReturn2SummaryActivity() { final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Intent.class); - BackgroundDataCondition backgroundDataCondition - = new BackgroundDataCondition(mConditionManager); - backgroundDataCondition.onPrimaryClick(); + mController.onPrimaryClick(mContext); verify(mContext).startActivity(argumentCaptor.capture()); Intent intent = argumentCaptor.getValue(); diff --git a/tests/robotests/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionControllerTest.java new file mode 100644 index 00000000000..8849e2d58f3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/conditional/v2/BatterySaverConditionControllerTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.IntentFilter; +import android.os.PowerManager; + +import com.android.settings.fuelgauge.BatterySaverReceiver; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowPowerManager; + +@RunWith(SettingsRobolectricTestRunner.class) +public class BatterySaverConditionControllerTest { + @Mock + private ConditionManager mConditionManager; + + private ShadowPowerManager mPowerManager; + private Context mContext; + private BatterySaverConditionController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class)); + mController = new BatterySaverConditionController(mContext, mConditionManager); + } + + @Test + public void startMonitor_shouldRegisterReceiver() { + mController.startMonitoringStateChange(); + + verify(mContext).registerReceiver(any(BatterySaverReceiver.class), any(IntentFilter.class)); + } + + @Test + public void stopMonitor_shouldUnregisterReceiver() { + mController.startMonitoringStateChange(); + mController.stopMonitoringStateChange(); + + verify(mContext).unregisterReceiver(any(BatterySaverReceiver.class)); + } + + @Test + public void isDisplayable_PowerSaverOn_true() { + mPowerManager.setIsPowerSaveMode(true); + + assertThat(mController.isDisplayable()).isTrue(); + } + + @Test + public void isDisplayable_PowerSaverOff_false() { + mPowerManager.setIsPowerSaveMode(false); + + assertThat(mController.isDisplayable()).isFalse(); + } + +} diff --git a/tests/robotests/src/com/android/settings/homepage/conditional/v2/WorkModeConditionControllerTest.java b/tests/robotests/src/com/android/settings/homepage/conditional/v2/WorkModeConditionControllerTest.java new file mode 100644 index 00000000000..04d58412883 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/conditional/v2/WorkModeConditionControllerTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 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.settings.homepage.conditional.v2; + +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.ComponentName; +import android.content.Context; + +import com.android.settings.Settings; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WorkModeConditionControllerTest { + + private Context mContext; + private WorkModeConditionController mController; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mController = new WorkModeConditionController(mContext); + } + + @Test + public void onPrimaryClick_shouldLaunchAccountsSetting() { + final ComponentName componentName = + new ComponentName(mContext, Settings.AccountDashboardActivity.class); + + mController.onPrimaryClick(mContext); + + verify(mContext).startActivity( + argThat(intent -> intent.getComponent().equals(componentName))); + } + +} diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java index 65f92a32727..d9bc486be5d 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiManager.java @@ -52,6 +52,11 @@ public class ShadowWifiManager extends org.robolectric.shadows.ShadowWifiManager return Collections.emptyList(); } + @Implementation + public boolean isDualModeSupported() { + return false; + } + public static ShadowWifiManager get() { return Shadow.extract(application.getSystemService(WifiManager.class)); } diff --git a/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java index b6b3d60748a..67d5dc60977 100644 --- a/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java +++ b/tests/robotests/src/com/android/settings/wallpaper/WallpaperSuggestionActivityTest.java @@ -17,14 +17,13 @@ package com.android.settings.wallpaper; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.app.WallpaperManager; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; -import com.android.settings.SubSettings; import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.After; @@ -35,14 +34,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; -import org.robolectric.shadows.ShadowActivity; -import org.robolectric.shadows.ShadowPackageManager; @RunWith(SettingsRobolectricTestRunner.class) public class WallpaperSuggestionActivityTest { @@ -65,20 +61,6 @@ public class WallpaperSuggestionActivityTest { ShadowWallpaperManager.reset(); } - @Test - public void launch_primarySuggestionActivityDoesNotExist_shouldFallback() { - ShadowPackageManager packageManager = - Shadows.shadowOf(RuntimeEnvironment.application.getPackageManager()); - packageManager.removePackage("com.android.settings"); - - ShadowActivity activity = Shadows.shadowOf(mController.setup().get()); - final Intent intent = activity.getNextStartedActivity(); - - assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName()); - assertThat(intent.getFlags()).isEqualTo(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - assertThat(activity.isFinishing()).isTrue(); - } - @Test public void wallpaperServiceEnabled_no_shouldReturnTrue() { when(mContext.getResources()).thenReturn(mResources); diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index d376775d1a3..4b765e845ed 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -17,6 +17,7 @@ package com.android.settings.wifi.tether; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -27,6 +28,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowWifiManager; import org.junit.Before; import org.junit.Test; @@ -34,12 +36,15 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) +@Config(shadows = {ShadowWifiManager.class}) public class WifiTetherSettingsTest { + private static final String[] WIFI_REGEXS = {"wifi_regexs"}; private Context mContext; @@ -55,12 +60,12 @@ public class WifiTetherSettingsTest { MockitoAnnotations.initMocks(this); doReturn(mConnectivityManager) .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); - doReturn(mUserManager) - .when(mContext).getSystemService(Context.USER_SERVICE); + doReturn(WIFI_REGEXS).when(mConnectivityManager).getTetherableWifiRegexs(); + doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE); } @Test - public void testWifiTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { + public void wifiTetherNonIndexableKeys_tetherAvailable_keysNotReturned() { // To let TetherUtil.isTetherAvailable return true, select one of the combinations setupIsTetherAvailable(true); @@ -74,7 +79,7 @@ public class WifiTetherSettingsTest { } @Test - public void testWifiTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { + public void wifiTetherNonIndexableKeys_tetherNotAvailable_keysReturned() { // To let TetherUtil.isTetherAvailable return false, select one of the combinations setupIsTetherAvailable(false); @@ -87,6 +92,12 @@ public class WifiTetherSettingsTest { assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); } + @Test + public void createPreferenceControllers_notEmpty() { + assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(mContext)) + .isNotEmpty(); + } + private void setupIsTetherAvailable(boolean returnValue) { when(mConnectivityManager.isTetheringSupported()).thenReturn(true);